自己動手寫代碼,記錄中間出現的錯誤。
目錄
遞歸遍歷是非常重要的,對於二叉樹的題目來說,雖然單獨讓你寫中序遍歷的時候最好不要用遞歸,但是其他題目中有用到很多。inorder(Node->left,res);一直遞歸到最左邊的null了,返回,此時壓入最左邊節點res.push_back(Node->val);然後看看有沒有右邊的節點,有的話帶進去, inorder(Node->right,res);然後又回到了找最左的過程,以此循環。有一個回溯的過程。
- 遞歸遍歷的過程是void函數,注意vector<int>& res,這裏的&符號。
- 注意Node->left的時候,要想if(node->left),這裏雖然不用加,但也要有思想的過程。
中序遍歷!
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
vector<int> res;// 全局變量放外面,遞歸
inorder(root,res);
return res;
}
void inorder(TreeNode * Node,vector<int> &res){
if (Node==nullptr) return;// basecase
inorder(Node->left,res);
res.push_back(Node->val);
inorder(Node->right,res);
}
};
class Solution {
public:
vector<int> inorderTraversal(TreeNode* root) {
TreeNode* cur = root;
vector<int> res;
stack<TreeNode *> helpStack;
while(!helpStack.empty()||cur!=nullptr){//記住這裏的條件
if(cur!=nullptr){// while裏面if就行
helpStack.push(cur);
cur = cur->left;
}
else{// 如果cur不爲空!!
cur = helpStack.top();
helpStack.pop();
res.push_back(cur->val);//注意是->val
cur = cur->right;// 彈出節點的右邊!!
}
}
return res;
}
};
先序遍歷
- 先把head放入stack中
- 循環while(!stack.empty())
- 彈出節點並打印
- 將彈出節點的右節點,左節點壓入
cur=stack.top();stack.pop();如果cur->right不爲空stack.push_back(cur->right);如果cur->left不爲空stack.push_back(cur->left);
後序遍歷
利用兩個棧的方式
- 先把head在循環外壓入s1,
- 當s1不爲空的時候,while(s1不爲空)
- 彈出s1到s2中,
- 彈出節點的左右一次壓入s1
- 最後彈出s2就是
按層遍歷
-
不分行打印( deque)
#include <deque>
using namespace std;
void PrintFromTopToBottom(BinaryTreeNode* pRoot)
{
if(pRoot == nullptr)
return;
deque<BinaryTreeNode *> dequeTreeNode;
dequeTreeNode.push_back(pRoot);
while(dequeTreeNode.size())//!!!!!!
{
BinaryTreeNode *pNode = dequeTreeNode.front();
dequeTreeNode.pop_front();
printf("%d ", pNode->m_nValue);
if(pNode->m_pLeft)// !!!
dequeTreeNode.push_back(pNode->m_pLeft);
if(pNode->m_pRight)
dequeTreeNode.push_back(pNode->m_pRight);
}
}
-
分行打印(queue)
void Print2(BinaryTreeNode* pRoot) {
if (pRoot == nullptr)
return;
queue<BinaryTreeNode*>q;
q.push(pRoot);
int len = 0;
while (!q.empty()) {//
len = q.size();// 實時更新len,表示這一層的個數!!
while (len--) {//
BinaryTreeNode *pCur = q.front();
q.pop();
cout << pCur->m_nValue<<" ";
if (pCur->m_pLeft != nullptr) {
q.push(pCur->m_pLeft);
}
if (pCur->m_pRight != nullptr) {
q.push(pCur->m_pRight);
}
}
cout << endl;
}
}
Z字遍歷(兩個棧)
void Print3(BinaryTreeNode* pRoot) {
if (pRoot == nullptr)
return;
stack<BinaryTreeNode*>s1;// 存放奇數層
stack<BinaryTreeNode*>s2;// 存放偶數層
s1.push(pRoot);
int len = 0;
while (!s1.empty()||!s2.empty())
{
while (!s1.empty()){
len = s1.size();
while(len--){
BinaryTreeNode* pCur = s1.top();
s1.pop();
cout << pCur->m_nValue<<" ";
if (pCur->m_pLeft!=nullptr)
s2.push(pCur->m_pLeft);
if (pCur->m_pRight != nullptr)
s2.push(pCur->m_pRight);
}
}
cout << endl;
while (!s2.empty()) {
len = s2.size();
while (len--) {
BinaryTreeNode* pCur = s2.top();
s2.pop();
cout << pCur->m_nValue << " ";
if (pCur->m_pRight != nullptr)
s1.push(pCur->m_pRight);
if (pCur->m_pLeft != nullptr)
s1.push(pCur->m_pLeft);
}
}
cout << endl;
}
}
Moris神級遍歷
鏡像二叉樹
將原先的二叉樹變成鏡像二叉樹
利用先序遍歷的方式,遞歸,開始寫basecase(如果是葉子結點了,直接返回)
主體部分:交換該節點的左右節點,如果有左子節點,遞歸交換左子節點的左右,如果有右子節點,遞歸交換右子節點的左右。
void MirrorRecursively(BinaryTreeNode *pNode)
{
if((pNode == nullptr) || (pNode->m_pLeft == nullptr && pNode->m_pRight))
return;
BinaryTreeNode *pTemp = pNode->m_pLeft;
pNode->m_pLeft = pNode->m_pRight;
pNode->m_pRight = pTemp;
if(pNode->m_pLeft)
MirrorRecursively(pNode->m_pLeft);
if(pNode->m_pRight)
MirrorRecursively(pNode->m_pRight);
}
void MirrorIteratively(BinaryTreeNode* pRoot)
{
if(pRoot == nullptr)
return;
std::stack<BinaryTreeNode*> stackTreeNode;
stackTreeNode.push(pRoot);
while(stackTreeNode.size() > 0)
{
BinaryTreeNode *pNode = stackTreeNode.top();
stackTreeNode.pop(); // 根左右,在根處交換左右
BinaryTreeNode *pTemp = pNode->m_pLeft;
pNode->m_pLeft = pNode->m_pRight;
pNode->m_pRight = pTemp;
if(pNode->m_pLeft)
stackTreeNode.push(pNode->m_pLeft);
if(pNode->m_pRight)
stackTreeNode.push(pNode->m_pRight);
}
}
是否是對稱二叉樹?( pRoot1, pRoot2)遞歸
參數是兩個(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
bool isSymmetrical(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2);
bool isSymmetrical(BinaryTreeNode* pRoot)
{
return isSymmetrical(pRoot, pRoot);
}
bool isSymmetrical(BinaryTreeNode* pRoot1, BinaryTreeNode* pRoot2)
{
if(pRoot1 == nullptr && pRoot2 == nullptr)//如果都相等
return true;
if(pRoot1 == nullptr || pRoot2 == nullptr)// 判斷條件
return false;
if(pRoot1->m_nValue != pRoot2->m_nValue)// 判斷條件
return false;
return isSymmetrical(pRoot1->m_pLeft, pRoot2->m_pRight)// 遞歸
&& isSymmetrical(pRoot1->m_pRight, pRoot2->m_pLeft);
}
是否是二叉搜索樹的後序數組?
遞歸,參數是(int arr[],length)
後序數組,先找到根節點,然後從開頭開始找比根節點小的數,然後在這之後到最後,如果還有比根節點小的數,則返回false。
遞歸的時候看左右是不是,其實還是用先序遍歷的方式~
5 7 6 9 11 10 8
bool VerifySquenceOfBST(int sequence[], int length)
{
if(sequence == nullptr || length <= 0)
return false;
int root = sequence[length - 1]; //更新出根節點value
// 在二叉搜索樹中左子樹的結點小於根結點
int i = 0;
for(; i < length - 1; ++ i)
{
if(sequence[i] > root)
break;
}
// 在二叉搜索樹中右子樹的結點大於根結點
int j = i;
for(; j < length - 1; ++ j)
{
if(sequence[j] < root)
return false;
}
// 判斷左子樹是不是二叉搜索樹
bool left = true;
if(i > 0)
left = VerifySquenceOfBST(sequence, i);// 遞歸,就是數組的範圍在變換
// 判斷右子樹是不是二叉搜索樹
bool right = true;
if(i < length - 1)
right = VerifySquenceOfBST(sequence + i, length - i - 1);
return (left && right);//左右是不是
}
[98] 驗證二叉搜索樹(BST)
/*
*
* 示例 1:
*
* 輸入:
* 2
* / \
* 1 3
* 輸出: true
*/
class Solution {
double last = LONG_MIN;//temp
public:
bool isValidBST(TreeNode* root) {
if (root == nullptr) {
return true;
}
if (isValidBST(root->left)) {//zuo
if (last < root->val) {//如果左節點滿足,
last = root->val;
return isValidBST(root->right);
}
}
return false;
}
};
class Solution {
public:
bool isValidBST(TreeNode* root) {
// 注意 用long的最大最小值
return check(root, LONG_MIN, LONG_MAX);
}
bool check(TreeNode* root, long min, long max){
if (root == NULL)
return true;
if (root->val <= min || root->val >= max)
return false;
return check(root->left, min, root->val) && check(root->right, root->val, max);
}
}
二叉樹的層高!
用後序遍歷的方式做。
站到頭結點,和左樹右樹,要信息
int getHeight(treeNode * head,int level)
{
//傳入頭結點,level爲0,返回的level,就是層高
if (head == nullptr) return level;
left_height = getHeight(head->left,level+1);//遞歸的狀態,只應用於本次循環,所以每一對leftright都是一對兒。
right_height= getHeight(head->right,level+1);//右樹高度
return max(left_height,right_height);
}
int getHeight(treeNode * head)
{
if (head == nullptr) return 0;
left_height = getHeight(head->left);//
right_height= getHeight(head->right);
return max(left_height,right_height)+1;
}
// 非遞歸,bfs通過隊列size得到本層的節點數目
int TreeDepth(TreeNode* pRoot)
{
queue<TreeNode*> q;
if(!pRoot) return 0;
q.push(pRoot);
int level=0;
while(!q.empty()){
int len=q.size();
level++;
while(len--){
TreeNode* tem=q.front();
q.pop();
if(tem->left) q.push(tem->left);
if(tem->right) q.push(tem->right);
}
}
return level;
}
二叉樹的左右邊界打印
輔助數據:一個二維數組
內部邏輯:中序遍歷,basecase考慮某一節點,if 什麼時候是左邊界,if 什麼時候是右邊界,然後遍歷左右
- 如果是第一次遍歷到這一層的edgemap[l][0]==nullptr;那麼它就是左邊界,edgemap[l][0]=cur
- 如果edgemap[l][1],把最後一次遍歷的cur給它就是右邊界,所以不用加判斷,一直edgemap[l][1]=cur,就好,循環到最後就是
凡是和層高有關係的,遞歸函數的形參裏面都有level
- setEdgeMap(h->left, l + 1);
- setEdgeMap(h->right, l + 1);
- int getHeight(treeNode * head,int level)
- {
- //傳入頭結點,level爲0,返回的level,就是層高
- if (head == nullptr) return level;
- left_height = getHeight(head->left,level+1);//遞歸的狀態,只應用於本次循環,所以每一對leftright都是一對兒。
- right_height= getHeight(head->right,level+1);//右樹高度
- return max(left_height,right_height);
- }
#include "stdafx.h"
#include"iostream"
using namespace std;
struct Node {
Node* left;
Node* right;
int data;
Node(int value):data(value), left(NULL), right(NULL){}
};
Node* T;
Node* edgeMap[100 + 5][2];
void setEdgeMap(Node* h, int l)
{
// 根左右,中序遍歷,先basecase再判斷,根節點的此處的邏輯,然後左右遍歷
if (h == NULL) return;
//左邊界 如果是第一次遇到就令它等於左邊界
if (edgeMap[l][0] == NULL)
edgeMap[l][0] = h;
//右邊界 是這一層最後一個遍歷到的,所以一直賦予就可以了,到最後就是最右邊界
edgeMap[l][1] = h;
setEdgeMap(h->left, l + 1);
setEdgeMap(h->right, l + 1);
}
int main()
{
Node* node1 = new Node(1);
Node*node2 = new Node(2);
Node*node3 = new Node(3);
Node*node4 = new Node(4);
Node*node5 = new Node(5);
Node*node6 = new Node(6);
Node*node7 = new Node(7);
Node*node8 = new Node(8);
Node*node9 = new Node(9);
Node*node10 = new Node(10);
Node*node11 = new Node(11);
Node*node12 = new Node(12);
Node*node13 = new Node(13);
Node*node14 = new Node(14);
Node*node15 = new Node(15);
Node*node16 = new Node(16);
node1->left = node2;
node1->right = node3;
node2->right = node4;
node3->left = node5;
node3->right = node6;
node4->left = node7;
node4->right = node8;
node5->left = node9;
node5->right = node10;
node8->right = node11;
node9->left = node12;
node11->left = node13;
node11->right = node14;
node12->left = node15;
node12->right = node16;
setEdgeMap(node1,0);
for (int i = 0; i < 6; i++)
{
cout << edgeMap[i][0]->data<<" ";
}
cout << endl;
for (int i = 0; i < 6; i++)
{
cout << edgeMap[i][1]->data << " ";
}
cout << endl;
return 0;
}
[96] 不同的二叉搜索樹
/* 給定 n = 3, 一共有 5 種不同結構的二叉搜索樹:
*
* 1 3 3 2 1
* \ / / / \ \
* 3 2 1 1 3 2
* / / \ \
* 2 1 2 3 */
class Solution {// 總結規律,從遍歷的過程。二叉搜索樹的中序遍歷是遞增的。
public:
int numTrees(int n) {
int f[n+1]={0};//注意一定要初始化!!!
f[0]=1;
for(int i=1;i<n+1;i++){
for(int j=1;j<i+1;j++){
f[i]=f[i]+f[j-1]*f[i-j];//這個就是下面那個的公式
}
}
return f[n];
}
};
tricks:
- 不寫累加符號什麼的,直接dp(n)=dp(0)*dp(n-1)+dp(1)*dp(n-2)+dp(2)*dp(n-3)+...+dp(n-1)*dp(0) ,好看。注意dp[0]=1.
- 注意dp(n)=dp(0)*dp(n-1)+dp(1)*dp(n-2)+dp(2)*dp(n-3)+...+dp(n-1)*dp(0) 到dp[n] + = dp[j-1]*dp[n-j] 內遍歷,j從1到n。然後到dp[i] + = dp[j-1]*dp[i-j],外遍歷
- 注意數組一定要初始化! 動態規劃的題目,有幾個變量就有幾個for,裏面一般就是if什麼的。
[112] 路徑總和
/*
* 給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。
*
* 5
* / \
* 4 8
* / / \
* 11 13 4
* / \ \
* 7 2 1
*
*
* 返回 true, 因爲存在目標和爲 22 的根節點到葉子節點的路徑 5->4->11->2。
*
*/
class Solution {
public:
bool hasPathSum(TreeNode* root, int sum) {
if (root == nullptr){
return false;
}
if(root->left==nullptr&&root->right==nullptr&&root->val==sum)
return true;
if (hasPathSum(root->left, sum-root->val))
return true;
if (hasPathSum(root->right, sum-root->val))
return true;
return false;
}
};
[113] 路徑總和 II
樹形dp套路
-
二叉樹節點間的最大距離問題!
-
派對的最大快樂值
-
最大搜索二叉子樹!
-
是否是平衡二叉樹BST
-
二叉樹 最近公共祖先
後繼節點
是指在中序遍歷中緊隨其後的節點
搜索二叉樹中,刪除一個值什麼的需要後繼節點的函數。
分爲三種情況:
1.一個節點有右孩子,則在中序遍歷中,該節點的後繼是它的右子樹的最左節點。
2. 這個節點是它父親的左孩子,則該節點的後繼節點是它的父親
3. 這個節點是它父親的右孩子,則需要一直向上搜索,直到它們n-1代祖先是它第n代祖先的左孩子,則它的後繼就是第n個祖先。如果一直搜索到根節點,也沒有找到n-1代祖先是它第n代祖先的左孩子,則該節點是整個樹的中序遍歷中的最後一個節點,即它沒有後繼。
protected Node getMinimum(Node node) {
while (node.left != null) {
node = node.left;
}
return node;
}
//找後繼節點
protected Node getSuccessor(Node node) {
// if there is right branch, then successor is leftmost node of that
// subtree
if (node.right != null) {//如果有右子節點,找右子節點最左邊的
return getMinimum(node.right);
} else { // otherwise it is a lowest ancestor whose left child is also
// ancestor of node//沒有右子節點找父節點
Node currentNode = node;
Node parentNode = node.parent;
while (parentNode != null && currentNode == parentNode.right) {
// go up until we find parent that currentNode is not in right
// subtree.
currentNode = parentNode;
parentNode = parentNode.parent;
}
return parentNode;
}
}
二叉樹 累積和爲k的路徑(先序遍歷做)
注意這裏的路徑,強調了必須是從根節點到葉節點的路徑纔算。
先序遍歷做(在根節點判斷滿不滿足,然後左右遍歷),遞歸,形參(root,vector,k,cursum)
每次更新的時候,
- 根節點處:需要更新cursum和vector,判斷cursum是不是等於k && 當前的節點是不是葉子節點,滿足條件遍歷vector,打印。
- 遍歷左右子節點:如果左子節點不爲空,遍歷遞歸左邊,如果右子節點不爲空,遍歷遞歸右邊。
- 在末尾,返回父函數的時候,吧vector和cursum減去當前的值。
void FindPath(BinaryTreeNode* pRoot, int expectedSum, std::vector<int>& path, int& currentSum);
void FindPath(BinaryTreeNode* pRoot, int expectedSum)
{
if(pRoot == nullptr)
return;
std::vector<int> path;
int currentSum = 0;
FindPath(pRoot, expectedSum, path, currentSum);
}
void FindPath
(
BinaryTreeNode* pRoot,
int expectedSum,
std::vector<int>& path,
int& currentSum
)
{ //更新參數
currentSum += pRoot->m_nValue;
path.push_back(pRoot->m_nValue);
// 如果是葉結點,並且路徑上結點的和等於輸入的值
// 打印出這條路徑
bool isLeaf = pRoot->m_pLeft == nullptr && pRoot->m_pRight == nullptr;
if(currentSum == expectedSum && isLeaf)
{
printf("A path is found: ");
std::vector<int>::iterator iter = path.begin();
for(; iter != path.end(); ++ iter)
printf("%d\t", *iter);
printf("\n");
}
// 如果有相應的節點,則遍歷它的子結點
if(pRoot->m_pLeft != nullptr)
FindPath(pRoot->m_pLeft, expectedSum, path, currentSum);
if(pRoot->m_pRight != nullptr)
FindPath(pRoot->m_pRight, expectedSum, path, currentSum);
// 在返回到父結點之前,在路徑上刪除當前結點,
// 並在currentSum中減去當前結點的值
currentSum -= pRoot->m_nValue;
path.pop_back();
}
二叉樹 累積和爲k的最長路徑
這道題需要數組裏累積和爲k的最長路徑作爲基礎,同樣是利用map,來做,不同的是,這裏開始時map[0]=0,表示什麼都不加的時候0層。先序遍歷做,和上一題差不多,在根節點處判斷,然後遍歷左右子節點,返回父節點的時候需要特別注意一下。
需要的參數 root,cursum,k,map,level,maxlen
int getMaxlen(BinaryTreeNode *root, int k) {
if (root == nullptr)return 0;
map<int, int>m;
m[0] = 0;
getMaxlen(root, k, 0, 1, 0, m);// 當前第一層
}
int getMaxlen(BinaryTreeNode *root, int k, int cursum, int level, int maxlen, map<int, int>m) {
cursum += root->m_nValue;
if (!m.count(cursum)) {
m[cursum] = level;
}
if (m.count(cursum - k)) {
maxlen = max(maxlen, level - m[cursum - k]);
}
maxlen = getMaxlen(root->m_pLeft, k, cursum, level + 1, maxlen, m);
maxlen = getMaxlen(root->m_pRight, k, cursum, level + 1, maxlen, m);
if ( m[cursum]==level ) {
m.erase(cursum);
}
return maxlen;
}
重構二叉樹
-
先序+中序
-
中序+後序
其他的情況我就不寫了,值得說明的是先序+後序重構二叉樹有些是重構不出來的。而上面的兩個,基本思路一致,我就只說明第一個了。注意這裏的二叉樹都不含重複的數字!
先序 根左右 1 2 4 7 3 5 6 8
中序 左根右 4 7 2 1 5 3 8 6
首先從先序中找到根,然後在中序中找到根的位置,以此判斷左子樹和右子樹。不斷遞歸。每次遞歸返回的都是當前狀態的根節點。
函數的參數有(先序開始,先序的結束,中序的開始,中序的結束)也是那種常見的數組的開頭和結尾,不斷縮短的形式
作用是:返回當前的根節點,並利用確定的左右子樹的範圍,構建左右子樹(利用遞歸函數)
// 需要先聲明
BinaryTreeNode* ConstructCore(int* startPreorder, int* endPreorder, int* startInorder, int* endInorder);
BinaryTreeNode* Construct(int* preorder, int* inorder, int length)
{
if(preorder == nullptr || inorder == nullptr || length <= 0)
return nullptr;
return ConstructCore(preorder, preorder + length - 1,
inorder, inorder + length - 1);
}
BinaryTreeNode* ConstructCore//注意這裏的參數,也是那種常見的數組的開頭和結尾,不斷縮短的形式
(
int* startPreorder, int* endPreorder,
int* startInorder, int* endInorder
)
{
// 前序遍歷序列的第一個數字是根結點的值
int rootValue = startPreorder[0];
BinaryTreeNode* root = new BinaryTreeNode();// 注意這裏構建的形式
root->m_nValue = rootValue;// 初始化
root->m_pLeft = root->m_pRight = nullptr;
// basecase 先序和中序數組都是剩下一個值,這個值必須還是相等的
if(startPreorder == endPreorder)
{
if(startInorder == endInorder && *startPreorder == *startInorder)
return root;
else
throw std::exception("Invalid input.");
}
// 在中序遍歷中找到根結點的值
int* rootInorder = startInorder;
while(rootInorder <= endInorder && *rootInorder != rootValue)
++ rootInorder;
if(rootInorder == endInorder && *rootInorder != rootValue)
throw std::exception("Invalid input.");
int leftLength = rootInorder - startInorder;
int* leftPreorderEnd = startPreorder + leftLength;
if(leftLength > 0)// 如果有左子樹
{
// 構建左子樹,注意這裏函數返回的是左子樹的根節點
root->m_pLeft = ConstructCore(startPreorder + 1, leftPreorderEnd,
startInorder, rootInorder - 1);
}// 如果有右子樹
if(leftLength < endPreorder - startPreorder)
{
// 構建右子樹
root->m_pRight = ConstructCore(leftPreorderEnd + 1, endPreorder,
rootInorder + 1, endInorder);
}
return root;
}