LeetCode+劍指 二叉樹總結

<font color=#FF0000 >紅色</font>
<font color=#008000 >綠色</font>
<font color=#0000FF >藍色</font>

ღღღ

二叉樹的遍歷總結

(前序、中序、後序、層序、 之字形層序、垂直遍歷)

三種遞歸遍歷

前序遍歷(根-左-右)
在這裏插入圖片描述

void preorder(TreeNode* root, vector<int>& path){
	if(root==NULL) return;
	path.push_back(root->val);  //打印根節點
	preorder(root->left,path);
	perorder(root->right,path);
}

中序遍歷(左-根-右)
在這裏插入圖片描述

void inorder(TreeNode* root, vector<int>& path){
	if(root==NULL) return; 
	inorder(root->left,path);
	path.push_back(root->val);
	inorder(root->right,path);
}

後續遍歷(左-右-根)
在這裏插入圖片描述

void postorder(TreeNode* root, vector<int>& path){
	if(root==NULL) return; 
	postorder(root->left,path);
	postorder(root->right,path);
	path.push_back(root->val);
}

三種非遞歸遍歷

前序遍歷(根-左-右)
根據前序遍歷訪問的順序,優先訪問根結點,然後再分別訪問左結點和右結點。即對於任一結點,其可看作是根結點,因此可以直接訪問,訪問完之後,若其左結點不爲空,按相同規則訪問它的左子樹;當訪問其左子樹時,再訪問它的右子樹。因此其處理過程如下:

    對於任一結點P:
     1)訪問結點P,並將結點P入棧;
     2)判斷結點P的左孩子是否爲空,若爲空,則取棧頂結點並進行出棧操作,並將棧頂結點的右孩子置爲當前的結點P,循環至1);若不爲空,則將P的左孩子置爲當前的結點P;
     3)直到P爲NULL並且棧爲空,則遍歷結束。
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
//非遞歸前序遍歷
class Solution{
public:
	vector<int> preorderTraversal(TreeNode* root){
		vector<int> path;
		if(root == nullptr) return path;
		stack<TreeNode*> s;
		TreeNode *p = root;
		while(p || !s.empty()){
			if(p){ //當左結點不爲空時
				path.push_back(p->val); //訪問當前結點(父結點)
				s.push(p); //入棧
				p = p->left; //指向下一個左結點
			}
			else{
				p = s.top();
				s.pop();         //出棧
				p = p->right;    //指向右結點
			}
		}
		return path;
	}
}

中序遍歷:(左 - 根 - 右)
根據中序遍歷的順序,對於任一結點,優先訪問其左孩子,而左孩子結點又可以看做一根結點,然後繼續訪問其左孩子結點,直到遇到左孩子結點爲空的結點才進行訪問,然後按相同的規則訪問其右子樹。因此其處理過程如下:

   對於任一結點P,
  1)若其左孩子不爲空,則將P入棧並將P的左孩子置爲當前的P,然後對當前結點P再進行相同的處理;
  2)若其左孩子爲空,則取棧頂元素並進行出棧操作,訪問該棧頂結點,然後將當前的P置爲棧頂結點的右孩子;
  3)直到P爲NULL並且棧爲空則遍歷結束
/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
//非遞歸中序遍歷
class Solution{
public:
	vector<int> inorderTraversal(TreeNode* root){
		vector<int> path;
		if(root == nullptr) return path;
		stack<TreeNode*> s;
		TreeNode *p = root;
		while(p || !s.empty()){
			if(p){ //當左結點不爲空時
				s.push(p); //入棧
				p = p->left; //指向下一個左結點
			}
			else{  //當左結點爲空時
				p = s.top();
				path.push_back(p->val); //訪問棧頂元素(父結點)
				s.pop();         //出棧
				
				p = p->right;    //指向右結點
			}
		}
		return path;			
	}
}

後序遍歷:(左 - 右 - 根)
要保證根結點在左孩子和右孩子訪問之後才能訪問,因此對於任一結點P,先將其入棧。

(1)如果P不存在左孩子和右孩子,則可以直接訪問它;
(2)或者P存在左孩子或者右孩子,但是其左孩子和右孩子都已被訪問過了,則同樣可以直接訪問該結點。
(3)若非上述兩種情況,則將P的右孩子和左孩子依次入棧,這樣就保證了每次取棧頂元素的時候,左孩子在右孩子前面被訪問,左孩子和右孩子都在根結點前面被訪問。
//非遞歸後序遍歷
vector<int> postorderTraversal(TreeNode* root){
	vector<int> result;
	stack<TreeNode*> s;
	if(root== nullptr) return result;
	TreeNode* p; //當前結點指針
	TreeNode* pre = nullptr; //用於記錄上一次訪問的結點
	s.push(root);            //根結點指針入棧
	while(!s.empty()){ //不爲空時纔會入棧,故p不可能爲nullptr,無需像之前加p的判斷
		p = s.top();   // 指向棧頂元素
		bool temp1 = p->left == nullptr && p->right == nullptr; //如果當前結點爲葉子結點
		bool temp2 = pre != nullptr && (pre == p->left || pre == p->right); //或者當前結點的左結點和右結點都已被訪問過了(若pre=p->left說明右結點爲空,因爲棧中按照根右左這樣的順序入棧,根左這種結構才能出現這種情況)
		
		if(!temp1 && !temp2)//如果不是上面兩種情況,直接入棧
		{
			//先將右結點入棧,再將左結點入棧,這樣可以保證之後訪問時先訪問左結點在訪問右結點
			if(p->right) s.push(p->right); //右結點入棧
			if(p->left) s.push(p->left);   //左結點入棧
		}
		else
		{
			result.push_back(p->val); //訪問順序:左、右、根
			s.pop();
			pre = p; //保存剛剛訪問過的結點
		}
	}
	return result;
}

參考:
《更簡單的非遞歸遍歷二叉樹的方法》

ღღღ

層次遍歷

(1)劍指32 - I. 從上到下打印二叉樹【中等】

從上到下打印出二叉樹的每個節點,同一層的節點按照從左到右的順序打印。

例如:
給定二叉樹: [3,9,20,null,null,15,7],
    3
   / \
  9  20
    /  \
   15   7
返回:
[3,9,20,15,7]

思路:層次遍歷。用一個隊列。

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<int> PrintFromTopToBottom(TreeNode* root) {
        vector<int> res;//保存要打印的數。
        if(root==NULL)
            return res;
        queue<TreeNode*> q;  //新建一個隊列。
        q.push(root);
        while(!q.empty()){
            res.push_back(q.front()->val);
            if(q.front()->left!=NULL)
                q.push(q.front()->left);
            if(q.front()->right!=NULL)
                q.push(q.front()->right);
            q.pop();  //彈出第一個元素。
        }
        return res;
    }
};

ღღღ

(2)劍指32 - II. 從上到下(分行)打印二叉樹 II

從上到下按層打印二叉樹,同一層的節點按從左到右的順序打印,每一層打印到一行。

例如:
給定二叉樹: [3,9,20,null,null,15,7],
    3
   / \
  9  20
    /  \
   15   7
返回其層次遍歷結果:
[
  [3],
  [9,20],
  [15,7]
]

思路:層次遍歷。用一個隊列。用一個臨時vector存放每一層的值。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;//保存要打印的數。注意vector裏面有vector
        if(root==NULL)//這個條件其實很重要,有時候不寫就出錯
            return res;
        queue<TreeNode*> q;  //新建一個隊列。
        q.push(root);  //先把根節點放進去
        while(!q.empty()){
            vector<int> tmp;  //定義一個臨時vector
            int len=q.size();//只能在這求,如果寫到for循環裏 for(int i = 0; i < q.size(); ++i),它大小會變化,結果出錯。
            for(int i = 0; i < len; ++i){
                tmp.push_back(q.front()->val);
                if(q.front()->left!=NULL)
                    q.push(q.front()->left);
                if(q.front()->right!=NULL)
                    q.push(q.front()->right);
                q.pop();  //彈出第一個元素。
            }
            res.push_back(tmp);
        }
        return res;
    }
};

ღღღ

(3)劍指32 - III. 從上到下(之字形)打印二叉樹 III(中等)

請實現一個函數按照之字形順序打印二叉樹,即第一行按照從左到右的順序打印,第二層按照從右到左的順序打印,第三行再按照從左到右的順序打印,其他行以此類推。

例如:
給定二叉樹: [3,9,20,null,null,15,7],
    3
   / \
  9  20
    /  \
   15   7
返回其層次遍歷結果:
[
  [3],
  [20,9],
  [15,7]
]

方法一:用一個隊列,雖然方便 ,但用reverse的時候複雜度要增加

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* root) {
        vector<vector<int>> res;//保存要打印的數。注意vector裏面有vector
        if(root==NULL)
            return res;
        queue<TreeNode*> q;  //新建一個隊列。
        q.push(root);  //先把根節點放進去
        bool flag=false;     //判斷是否爲偶數行,flag=false代表奇數行,flag=true代表偶數行
        while(!q.empty()){
            vector<int> tmp;  //定義一個臨時vector。行容器,用於存入當前行輸出的結果
            int len=q.size();
            for(int i = 0; i < len; ++i){
                tmp.push_back(q.front()->val);
                if(q.front()->left!=NULL)
                    q.push(q.front()->left);
                if(q.front()->right!=NULL)
                    q.push(q.front()->right);
                q.pop();  //彈出第一個元素。
            }
            if(flag) reverse(tmp.begin(),tmp.end());  //是偶數行就反轉順序
            flag=!flag;     //改變flag的值
            res.push_back(tmp);
        }
        return res;
    }
};

方法二:用兩個棧,時間換空間

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    vector<vector<int>> levelOrder(TreeNode* pRoot) {
    vector<vector<int> >  vec; //存放結果
    if(pRoot == NULL) return vec; //這個東西必須寫,否則超時!!!
    stack<TreeNode*> stk1, stk2; //定義兩個棧
    stk1.push(pRoot);
    vector<int> tmp;
    while(!stk1.empty() || !stk2.empty()){
        while(!stk1.empty()){  //如果棧1不爲空 //while 
            TreeNode* pNode = stk1.top();
            tmp.push_back(pNode->val);  //tmp.push(skt1.top()->left
            if(pNode->left) stk2.push(pNode->left);
            if(pNode->right) stk2.push(pNode->right);
            stk1.pop();
        }
        if(tmp.size()){ //if
            vec.push_back(tmp);
            tmp.clear();
        }
        while(!stk2.empty()){  //如果棧2不爲空
            TreeNode* pNode = stk2.top();
            tmp.push_back(pNode->val);
            if(pNode->right) stk1.push(pNode->right); //注意,放入的順序要反
            if(pNode->left) stk1.push(pNode->left);
            stk2.pop();
        }
        if(tmp.size()){
            vec.push_back(tmp);
            tmp.clear();
        }
    }
    return vec;
    }
};

ღღღ

(4)314.二叉樹的豎直遍歷【中等】☒ (上鎖)

還不會,不懂pair是啥類型。

Examples 1:
Input: [3,9,20,null,null,15,7]
   3
  /\
 /  \
 9  20
    /\
   /  \
  15   7 
Output:
[
  [9],
  [3,15],
  [20],
  [7]
]
Examples 2:
Input: [3,9,8,4,0,1,7]
     3
    /\
   /  \
   9   8
  /\  /\
 /  \/  \
 4  01   7 
Output:
[
  [4],
  [9],
  [3,0,1],
  [8],
  [7]
]
Examples 3:
Input: [3,9,8,4,0,1,7,null,null,null,2,5] (0's right child is 2 and 1's left child is 5)
     3
    /\
   /  \
   9   8
  /\  /\
 /  \/  \
 4  01   7
    /\
   /  \
   5   2
Output:
[
  [4],
  [9,5],
  [3,0,1],
  [8,2],
  [7]
]
/* 掌握
問題:二叉樹的垂直遍歷
方法:層序遍歷,並給每個結點賦上列號(對於每列元素而言,層序遍歷訪問的先後順序滿足垂直遍歷規律)
把根節點給個序號0,然後開始層序遍歷,
凡是左子節點則序號減1,右子節點序號加1,
這樣我們可以通過序號來把相同列的節點值放到一起
*/
class Solution
{
public:
    vector<vector<int>> verticalOrder(TreeNode* root)
    {
        vector<vector<int>> res;
        if (!root) return res;
        
        map<int, vector<int>> m; //構建存儲<序號,遍歷序列>對的map
        queue<pair<int, TreeNode*>> q; //構建存儲<序號,結點>對的隊列
        
        q.push({0, root}); //根結點入隊,根結點序號設爲0
        while (!q.empty()) //層序遍歷
        {
            auto a = q.front();
            m[a.first].push_back(a.second->val); //訪問當前結點,將結點值push到相同列的容器中
            q.pop(); //出隊
           
            
            //將下一層結點入隊
            if (a.second->left) q.push( {a.first - 1, a.second->left} ); //左結點序號減一
            if (a.second->right) q.push( {a.first + 1, a.second->right} ); //右結點序號加一
            //下一層的結點排在上一層結點之後
        }
        
        for (auto mi : m)  //將map中遍歷序列按順序push到結果容器中(map內部會自動排序,序號從小到大排列遍歷序列)
        {
            res.push_back(mi.second);
        }
        return res;
    }
};

ღღღ

(5)劍指55 - I. 二叉樹的深度(LC104)

輸入一棵二叉樹的根節點,求該樹的深度。從根節點到葉節點依次經過的節點(含根、葉節點)形成樹的一條路徑,最長路徑的長度爲樹的深度。
例如:
給定二叉樹 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7
返回它的最大深度 3 。

遞歸

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int maxDepth(TreeNode* pRoot) {
        if(pRoot==NULL){//遞歸的出口
            return 0;
        }
        int nleft=maxDepth(pRoot->left);//用遞歸的方法,左子樹的深度
        int nright=maxDepth(pRoot->right); //右子樹的深度
        return (nleft>nright)? (nleft+1):(nright+1); //更大的那個值+1
        //return 1 + max(depth_left, depth_right);
    }
};

手寫遞歸過程分析:
在這裏插入圖片描述
ღღღ

(6)劍指55 - II. 平衡二叉樹(LC110)

輸入一棵二叉樹的根節點,判斷該樹是不是平衡二叉樹。如果某二叉樹中任意節點的左右子樹的深度相差不超過1,那麼它就是一棵平衡二叉樹。

示例 1:
給定二叉樹 [3,9,20,null,null,15,7]
    3
   / \
  9  20
    /  \
   15   7
返回 true 。

示例 2:
給定二叉樹 [1,2,2,3,3,null,null,4,4]
       1
      / \
     2   2
    / \
   3   3
  / \
 4   4
返回 false

法1:遞歸,先序遍歷

(好理解)先序遍歷每一個節點,並比較左右子樹高度,如果有>1則返回false (在上一題 “二叉樹的深度” 的基礎上進行判斷)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isBalanced(TreeNode* root) {
        if (root == NULL) return true;//如果該子樹爲空,則一定是平衡的(因爲沒有左右子樹)
		if (abs(getHeight(root->left) - getHeight(root->right)) > 1) return false;
		return isBalanced(root->left)&& isBalanced(root->right);  //都返回true,則返回true
    }
        //獲取深度  套路
        int getHeight(TreeNode* pRoot) {
        if(pRoot==NULL){//遞歸的出口
            return 0;
        }
        int nleft=getHeight(pRoot->left);//用遞歸的方法,左子樹的深度
        int nright=getHeight(pRoot->right); //右子樹的深度
        return (nleft>nright)? (nleft+1):(nright+1); //更大的那個值+1
        //return 1 + max(depth_left, depth_right);
    }
};

法2 (遞歸)(不好理解)/後序遍歷/使用後序遍歷的方式遍歷二叉樹的每個節點,那麼在遍歷到一個節點之前我們就已經遍歷了它的左右子樹。只要在每個節點的時候記錄它的深度(某一節點的深度等於它到葉節點的路徑的長度)就可以判斷每個節點是不是平衡的

class Solution {
public:
    bool IsBalanced_Solution(TreeNode* pRoot) {
    	if (pRoot== nullptr) return true;
        int depth=0;
        return IsBalanced(pRoot,depth);
    }
    
    bool IsBalanced(TreeNode* pRoot,int &depth){
        if(pRoot==NULL)
            return true;
        int left=0;
        int right=0;
        if(IsBalanced(pRoot->left,left) && IsBalanced(pRoot->right,right)){
            int diff=left-right;
            if(diff<=1 && diff>=-1){  //abs(diff) <= 1
                depth=(left>right? left:right)+1;
                return true;
            }
        }
        return false;
    }
};

ღღღ

(7)劍指28. 對稱的二叉樹(LC101)

請實現一個函數,用來判斷一棵二叉樹是不是對稱的。如果一棵二叉樹和它的鏡像一樣,那麼它是對稱的。

例如,二叉樹 [1,2,2,3,4,4,3] 是對稱的。
    1
   / \
  2   2
 / \ / \
3  4 4  3

但是下面這個 [1,2,2,null,3,null,3] 則不是鏡像對稱的:
    1
   / \
  2   2
   \   \
   3    3
示例 1:
輸入:root = [1,2,2,3,4,4,3]
輸出:true
示例 2:
輸入:root = [1,2,2,null,3,null,3]
輸出:false

遞歸

遞歸結束條件:
都爲空指針則返回 true
只有一個爲空則返回 false
兩個指針當前節點值不相等 返回false
遞歸過程:
判斷 A 的右子樹與 B 的左子樹是否對稱
判斷 A 的左子樹與 B 的右子樹是否對稱

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        return isMirror(root,root);  //把兩個根節點傳入,相當於有兩個相同的二叉樹
    }
    bool isMirror(TreeNode* proot1, TreeNode* proot2){
        if(proot1==NULL && proot2==NULL) return true;//遞歸的三個出口
        if(proot1==NULL || proot2==NULL) return false;// 如果其中之一爲空,也不是對稱的
         // 走到這裏二者一定不爲空
        if(proot1->val != proot2->val) return false;
        //左結點的左子樹和右結點的右子樹相等,左結點的右子樹和右結點的左子樹相等時返回turn
        return isMirror(proot1->left,proot2->right) && isMirror(proot1->right,proot2->left);
    }
};

手畫遞歸過程:
在這裏插入圖片描述
ღღღ

(8)劍指27. 二叉樹的鏡像(LC226)

請完成一個函數,輸入一個二叉樹,該函數輸出它的鏡像。

例如輸入:
     4
   /   \
  2     7
 / \   / \
1   3 6   9
鏡像輸出:
     4
   /   \
  7     2
 / \   / \
9   6 3   1

示例 1:
輸入:root = [4,2,7,1,3,6,9]
輸出:[4,7,2,9,6,3,1]

遞歸

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* pRoot) {
         if (pRoot == NULL) return NULL;
         //if (pRoot->left==NULL&&pRoot->right==NULL) return;
         swap(pRoot->left, pRoot->right);
         mirrorTree(pRoot->left);
         mirrorTree(pRoot->right);

         return pRoot;
    }
};

模型:棧模擬二叉樹的先序遍歷
循環結束條件:棧爲空
實現操作:交換當前結點的左右子樹

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* pRoot) {
         if (pRoot == NULL)return NULL;
         stack<TreeNode*> st;
         //TreeNode* p = NULL;
         st.push(pRoot);
         while (st.size())
         {
             TreeNode* p = st.top();
             st.pop();
             swap(p->left, p->right);
             if (p->left)st.push(p->left);
             if (p->right)st.push(p->right);
         }
        return pRoot;
    }
};

同理,隊列

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* mirrorTree(TreeNode* pRoot) {
         if (pRoot == NULL)return NULL;
         queue<TreeNode*> st;
         //TreeNode* p = NULL;
         st.push(pRoot);
         while (st.size())
         {
             TreeNode* p = st.front();
             st.pop();
             swap(p->left, p->right);
             if (p->left)st.push(p->left);
             if (p->right)st.push(p->right);
         }
        return pRoot;
    }
};

ღღღ

(9)劍指26. 樹的子結構【中等】(LC572 no)

輸入兩棵二叉樹A和B,判斷B是不是A的子結構。(約定空樹不是任意一個樹的子結構)
B是A的子結構, 即 A中有出現和B相同的結構和節點值。

例如:
給定的樹 A:

     3
    / \
   4   5
  / \
 1   2
給定的樹 B:

   4 
  /
 1
返回 true,因爲 B 與 A 的一個子樹擁有相同的結構和節點值。

示例 1:
輸入:A = [1,2,3], B = [3,1]
輸出:false
示例 2:
輸入:A = [3,4,5,1,2], B = [4,1]
輸出:true

遞歸

思路:
首先要遍歷A找出與B根節點一樣值的節點R
然後判斷樹A中以R爲根節點的子樹是否包含和B一樣的結構

class Solution {
public:
    bool isSubStructure(TreeNode* A, TreeNode* B) {
        bool res = false;
        //當TreeA和TreeB都不爲零的時候,才進行比較。否則直接返回false
        if (A!=NULL && B!=NULL)
        {
            //如果找到了對應TreeB的根節點的點
            if (A->val == B->val)
                //以這個根節點爲爲起點判斷是否包含TreeB
                res = helper(A, B);
            //如果找不到,那麼就再去TreeA的左子樹當作起點,去判斷是否包含TreeB
            if (!res)
                res = isSubStructure(A->left, B);
            //如果還找不到,那麼就再去TreeA的右子樹當作起點,去判斷是否包含TreeB
            if (!res)
                res = isSubStructure(A->right, B);
        }
        // 返回結果
        return res;
    }

    bool helper(TreeNode* A, TreeNode* B)
    {
        //如果TreeB已經遍歷完了都能對應的上,返回true
        if (B==NULL)
            return true;
        //如果TreeB還沒有遍歷完,TreeA卻遍歷完了。返回false
        if (A==NULL)
            return false;
        //如果其中有一個點沒有對應上,返回false
        if (A->val != B->val)
            return false;
        //如果根節點對應的上,那麼就分別去子節點裏面匹配
        return helper(A->left, B->left) && helper(A->right, B->right);
    }
};

ღღღ

(10)劍指34. 二叉樹中和爲某一值的路徑【中等】(LC113)

輸入一棵二叉樹和一個整數,打印出二叉樹中節點值的和爲輸入整數的所有路徑。從樹的根節點開始往下一直到葉節點所經過的節點形成一條路徑。要求輸出所有路徑

示例:
給定如下二叉樹,以及目標和 sum = 22,
              5
             / \
            4   8
           /   / \
          11  13  4
         /  \    / \
        7    2  5   1
返回:
[
   [5,4,11,2],
   [5,8,4,5]
]

思路:遞歸。先序遍歷/深度優先搜索/回溯

/*
struct TreeNode {
	int val;
	struct TreeNode *left;
	struct TreeNode *right;
	TreeNode(int x) :
			val(x), left(NULL), right(NULL) {
	}
};*/
class Solution {
public:
    vector<vector<int> > FindPath(TreeNode* root,int expectNumber) {
    find(root, expectNumber);
        return res;
    }

    vector<vector<int>> res;
    vector<int> path;
    void find(TreeNode* root,  int sum)
    {
        if (root == NULL)return;  //啥都不幹,返回上一步
        path.push_back(root->val);
        if (!root->left && !root->right && sum == root->val)//!root->left && !root->right 兩個條件是保證更新到了最下面的葉結點。                                                                
            res.push_back(path); //所有的一下全放進去
        else
        {
            if (root->left)
                find(root->left, sum - root->val);
            if (root->right)
                find(root->right, sum - root->val);
        }
        path.pop_back(); // 回溯su   當成棧使用了
    }
};

ღღღ

(11)LC112. 路徑總和

給定一個二叉樹和一個目標和,判斷該樹中是否存在根節點到葉子節點的路徑,這條路徑上所有節點值相加等於目標和。
說明: 葉子節點是指沒有子節點的節點。不需要輸出路徑

示例: 
給定如下二叉樹,以及目標和 sum = 22,

              5
             / \
            4   8
           /   / \
          11  13  4
         /  \      \
        7    2      1
返回 true, 因爲存在目標和爲 22 的根節點到葉子節點的路徑 5->4->11->2。

注意:在bool函數中,必須有return什麼什麼,,return true, return false

思路:深度優先搜索# 只要求返回true或false,只判斷是否存在,因此不需要記錄路徑

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool DFS(TreeNode* root, int sum){
        if(root == NULL){
            return false;
        }
        if(!root->left &&!root->right){
            if(root->val == sum){
                return true;
            }
        }
        return (DFS(root->left, sum-root->val) || DFS(root->right, sum -root->val));  //只要有一個路徑通就行
    }
    bool hasPathSum(TreeNode* root, int sum) {
        return DFS(root, sum);
    }
};

或寫成

class Solution {
public:
    bool hasPathSum(TreeNode* root, int sum) {
        if(root == NULL){
            return false;  //都走到子節點爲空了,肯定爲false了
        }
        if(!root->left &&!root->right){
            if(root->val == sum){
                return true;
            }
        }
        return (hasPathSum(root->left, sum-root->val) || hasPathSum(root->right, sum -root->val));
    }
};

ღღღ

(12)124. 二叉樹中的最大路徑和【困難】

給定一個非空二叉樹,返回其最大路徑和。
本題中,路徑被定義爲一條從樹中任意節點出發,達到任意節點的序列。該路徑至少包含一個節點,且不一定經過根節點。

示例 1:
輸入: [1,2,3]
       1
      / \
     2   3
輸出: 6
示例 2:

輸入: [-10,9,20,null,null,15,7]
   -10
   / \
  9  20
    /  \
   15   7
輸出: 42

思路:

果然是hard,看了半天!

    最大路徑和:根據當前節點的角色,路徑和可分爲兩種情況:
    一:以當前節點爲根節點
    1.只有當前節點
    2.當前節點+左子樹
    3.當前節點+右子書
    4.當前節點+左右子樹    
    這四種情況的最大值即爲以當前節點爲根的最大路徑和
    此最大值要和已經保存的最大值比較,得到整個樹的最大路徑值
    
    二:當前節點作爲父節點的一個子節點
    和父節點連接的話則需取【單端的最大值】
    1.只有當前節點
    2.當前節點+左子樹
    3.當前節點+右子書
    這三種情況的最大值    
    """

代碼:

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int res=INT_MIN;
    int maxPathSum(TreeNode* root) {
        maxValue(root);
        return res;
    }
    int maxValue( TreeNode* root){
        if(root==NULL) return 0;  //遞歸出口
        int leftValue=maxValue(root->left);  //遞歸結果當然是整數了
        int rightValue=maxValue(root->right);
        int value1=root->val;
        int value2=root->val + leftValue;
        int value3=root->val + rightValue;
        int value4=root->val + leftValue + rightValue;

        int maxValue=max(max(max(value1,value2),value3),value4);//4個值的最大值
        res = max(maxValue, res);   //更新全局最大值
        return max(max(value1,value2),value3);//因爲要返回到父節點,所以返回單端的最大值.只能取left和right中較大的那個值,而不是兩個都要
    }
};

遞歸看不懂的一定要畫過程:
在這裏插入圖片描述
ღღღ

(13)劍指8 二叉樹(中序遍歷)的下一個結點(LC無)

題目描述
給定一個二叉樹和其中的一個結點,請找出中序遍歷順序的下一個結點並且返回。注意,樹中的結點不僅包含左右子結點,同時包含指向父結點的指針。
在這裏插入圖片描述
分析二叉樹的下一個節點,一共有以下情況:
1.二叉樹爲空,則返回空;
2.節點右孩子存在,則設置一個指針從該節點的右孩子出發,一直沿着指向左子結點的指針找到的葉子節點即爲下一個節點;比如b的下個節點是h,a的下個節點是f。
3.節點不是根節點。如果該節點是其父節點的左孩子,則返回父節點;否則繼續向上遍歷其父節點的父節點,重複之前的判斷,返回結果。代碼如下:

/*
struct TreeLinkNode {
    int val;
    struct TreeLinkNode *left;
    struct TreeLinkNode *right;
    struct TreeLinkNode *next;
    TreeLinkNode(int x) :val(x), left(NULL), right(NULL), next(NULL)     
    }
};
*/
class Solution {
public:
    TreeLinkNode* GetNext(TreeLinkNode* pNode)
    {
     if(pNode==NULL)
            return NULL;
        if(pNode->right!=NULL)//右節點存在。有右子樹時,下個結點爲右子樹最左結點
        {
            pNode=pNode->right;
            while(pNode->left!=NULL) //一直沿着指向左子結點的指針找到的葉子節點即爲下一個節點
                pNode=pNode->left;
            return pNode;
        }  
        while(pNode->next!=NULL)//無右子樹時,返回父結點(向上查找,結點爲左孩子的父結點)
        {
            TreeLinkNode *proot=pNode->next;  //next指針指向節點的父節點
            if(proot->left==pNode)
                return proot;//返回父結點
            pNode=pNode->next; //一直沿着父節點走
        }
        return NULL;
    }
};

ღღღ

(14)劍指37.序列化二叉樹【困難】(LC297)

知識點:string專門提供了一個名爲c_str()的成員函數。
c_str函數的返回值是一個C風格的字符串,也就是說,函數的返回值是一個指針。見C++ primer P111
const_cast 見C++primer P145

請實現兩個函數,分別用來序列化和反序列化二叉樹。

示例: 
你可以將以下二叉樹:
    1
   / \
  2   3
     / \
    4   5
序列化爲 "[1,2,3,null,null,4,5]"

二叉樹的序列化是指:把一棵二叉樹按照某種遍歷方式的結果以某種格式保存爲字符串,從而使得內存中建立起來的二叉樹可以持久保存。序列化可以基於先序、中序、後序、層序的二叉樹遍歷方式來進行修改,序列化的結果是一個字符串,序列化時通過 某種符號表示空節點(#),以 ! 表示一個結點值的結束(value!)。
二叉樹的反序列化是指:根據某種遍歷順序得到的序列化字符串結果str,重構二叉樹

/*
struct TreeNode {
    int val;
    struct TreeNode *left;
    struct TreeNode *right;
    TreeNode(int x) :
            val(x), left(NULL), right(NULL) {
    }
};
*/
class Solution {
public:
    //序列化////////////////////////////////////////////////////////////////////
    string sHelper(TreeNode *node)
    {
        if (node == NULL)
            return "N";
        return to_string(node->val) + "," +
                sHelper(node->left) + "," +
                sHelper(node->right);//按前序遍歷來序列化
    }
 
    char* Serialize(TreeNode *root)
    {
        string s = sHelper(root);  
        char *ret = new char[s.length()+1];
        strcpy(ret, const_cast<char*>(s.c_str())); //因爲函數要求返回的是char* 所以需要類型的轉換。
        return ret;
    }
或者這樣轉化類型:
/*
    char* Serialize(TreeNode *root)
    {
        string s = sHelper(root); //得到string類型的串   
        int length = s.length(); //計算s的長度
        char *ret = new char[length+1];  //+1是存放最後'\0'
        // 把str流中轉換爲字符串返回
        for(int i = 0; i < length; i++){
            ret[i] = s[i];
        }
        ret[length] = '\0';
        return ret;
*/
 
    //反序列化////////////////////////////////////////////
    TreeNode *dHelper(stringstream &ss)
    {
        string str;
        getline(ss, str, ',');
        if (str == "N")
            return NULL;
        else
        {
            TreeNode *node = new TreeNode(stoi(str));
            node->left = dHelper(ss);
            node->right = dHelper(ss);
            return node;
        }
    }
 
    TreeNode* Deserialize(char *str) {
        stringstream ss(str);
        return dHelper(ss);
    }
};

不需要轉換類型的代碼:

class Codec {
public:
    // Encodes a tree to a single string.
    string serialize(TreeNode* root) {  //因返回值爲string類型,所以不需要轉換類型
        string s = sHelper(root);
        return s;
    }
    string sHelper(TreeNode *node){
        if (node == NULL)
            return "N";
        return to_string(node->val) + "," +
                sHelper(node->left) + "," +
                sHelper(node->right);//按前序遍歷來序列化
    }

    // Decodes your encoded data to tree.
    TreeNode* deserialize(string str) {
        stringstream ss(str);
        return dHelper(ss);
    }

    TreeNode *dHelper(stringstream &ss)
    {
        string str;
        getline(ss, str, ',');
        if (str == "N")
            return NULL;
        else
        {
            TreeNode *node = new TreeNode(stoi(str));
            node->left = dHelper(ss);
            node->right = dHelper(ss);
            return node;
        }
    }
};

✍✍✍
重建二叉樹知識點:
二叉樹遍歷的兩個性質:
已知前序遍歷和中序遍歷,可以唯一確定一顆二叉樹;
已知後序遍歷和中序遍歷,可以唯一確定一顆二叉樹。
但是要注意了,已知前序和中序,是不能確定一顆二叉樹的!

例1:
在這裏插入圖片描述
例2:
在這裏插入圖片描述
例3:
在這裏插入圖片描述
ღღღ

(15)劍指07.重建二叉樹(已知前序和中序)【中等】(LC105)

根據一棵樹的前序遍歷與中序遍歷構造二叉樹。
注意:你可以假設樹中沒有重複的元素。

例如,給出
前序遍歷 preorder = [3,9,20,15,7]
中序遍歷 inorder = [9,3,15,20,7]
返回如下的二叉樹:
    3
   / \
  9  20
    /  \
   15   7

思路

雖然好理解,但是複雜度有點高!

思路:
由先序序列第一個pre[0]在中序序列中找到根節點位置gen
以gen爲中心遍歷
0~gen左子樹
子中序序列:0~gen-1,放入vin_left[]
子先序序列:1~gen放入pre_left[],+1可以看圖,因爲頭部有根節點
gen+1~vinlen爲右子樹
子中序序列:gen ~ vinlen-1放入vin_right[]
子先序序列:gen ~ vinlen-1放入pre_right[]
由先序序列pre[0]創建根節點
連接左子樹,按照左子樹子序列遞歸(pre_left[]和vin_left[])
連接右子樹,按照右子樹子序列遞歸(pre_right[]和vin_right[])
返回根節點

畫圖理解爲什麼這兒是i+1

pre_left.push_back(pre[i+1]);//先序第一個爲根節點

在這裏插入圖片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public: 
    TreeNode* buildTree(vector<int> pre, vector<int> vin) {
        //int vinlen=vin.size();
        if(pre.size()==0||vin.size()==0)
        	return NULL;
        vector<int> pre_left, pre_right, vin_left, vin_right;

        //創建根節點,根節點肯定是前序遍歷的第一個數
        TreeNode* head = new TreeNode(pre[0]);

        //找到中序遍歷根節點所在位置,存放於變量gen中
        int gen=0;
        for(int i=0;i<vin.size();i++){
            if(vin[i]==pre[0]){
                gen=i;
                break;
            }
        }
        //對於中序遍歷,根節點左邊的節點位於二叉樹的左邊,根節點右邊的節點位於二叉樹的右邊
        // 左子樹
        for(int i = 0; i < gen; i++){
            vin_left.push_back(vin[i]);
            pre_left.push_back(pre[i+1]);//先序第一個爲根節點
        }
        // 右子樹
        for(int i = gen + 1; i < vin.size(); i++){
            vin_right.push_back(vin[i]);
            pre_right.push_back(pre[i]);
        }
        //遞歸,執行上述步驟,區分子樹的左、右子子樹,直到葉節點
        head->left = buildTree(pre_left, vin_left);
        head->right = buildTree(pre_right, vin_right);
        return head;
    }    
};

ღღღ

(16)LC106.重建二叉樹(已知中序和後序)【中等】

根據一棵樹的中序遍歷與後序遍歷構造二叉樹。
注意:你可以假設樹中沒有重複的元素。

例如,給出
中序遍歷 inorder = [9,3,15,20,7]
後序遍歷 postorder = [9,15,7,20,3]
返回如下的二叉樹:
    3
   / \
  9  20
    /  \
   15   7

思路:與上題基本相同

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
        if(inorder.size()==0 || postorder.size()==0) return NULL;
        int len=postorder.size();//方便下面運用
        TreeNode* head=new TreeNode(postorder[len-1]);
        vector<int> in_left, post_left, in_right, post_right;
        // 在中序序列中找到根節點位置
        int gen=0;
        for(int i=0; i<inorder.size(); i++){
            if(inorder[i]==postorder[len-1]){
                gen=i;
                break;
            }
        }
        for(int i=0; i<gen; i++){
            in_left.push_back(inorder[i]);
            post_left.push_back(postorder[i]);
        }
        for(int i=gen+1; i<inorder.size(); i++){
            in_right.push_back(inorder[i]);
            post_right.push_back(postorder[i-1]);
        }
        head->left=buildTree(in_left, post_left);
        head->right=buildTree(in_right, post_right);
        return head;
    }
};

ღღღ

(17)98. 驗證二叉搜索樹【中等】

假設一個二叉搜索樹具有如下特徵:

節點的左子樹只包含小於當前節點的數。
節點的右子樹只包含大於當前節點的數。
所有左子樹和右子樹自身必須也是二叉搜索樹。
示例 1:

輸入:
    2
   / \
  1   3
輸出: true

法2,中序遍歷後,判斷排序前和排序後是否相同

自己發明的。好理解。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isValidBST(TreeNode* root) {
        vector<int> vec; 
        inorder(root,vec);
        vector<int> pvec(vec); //保存原始序列爲pvec
        sort(vec.begin(),vec.end()); //對原始序列排序
        for(int i=1; i<vec.size(); i++){//檢查是否有重複元素
            if(vec[i]==vec[i-1]) return false;
        }
        if(pvec==vec) return true;//排序前後對比
        return false;
    }
    void inorder(TreeNode* root,vector<int>& vec){
        if(root==NULL) return;
        inorder(root->left,vec);
        vec.push_back(root->val);
        inorder(root->right,vec);
    }
};

法1,思路:用中序遍歷迭代法做,二叉查找樹性質:中序遍歷後,二叉查找樹爲升序排列

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool isValidBST(TreeNode* root) {
        TreeNode* pre=NULL;
		stack<TreeNode*> s;
		TreeNode *p = root;
		while(p || !s.empty()){
			if(p){ //當左結點不爲空時
				s.push(p); //入棧
				p = p->left; //指向下一個左結點
			}
			else{  //當左結點爲空時
				p = s.top();
                if(pre!=NULL && p->val <= pre->val) return false;//看是否爲升序
                pre=p; //pre實際上是指向了前一個數
				s.pop();         //出棧
				
				p = p->right;    //指向右結點
			}
		}
		return true;	
    }
};

//參考 非迭代中序遍歷:

		while(p || !s.empty()){
			if(p){ //當左結點不爲空時
				s.push(p); //入棧
				p = p->left; //指向下一個左結點
			}
			else{  //當左結點爲空時
				p = s.top();
				path.push_back(p->val); //訪問棧頂元素(父結點)
				s.pop();         //出棧
				
				p = p->right;    //指向右結點
			}
		}

ღღღ

(18)劍指36. 二叉搜索樹與雙向鏈表【中等】(LC426)

輸入一棵二叉搜索樹,將該二叉搜索樹轉換成一個排序的循環雙向鏈表。要求不能創建任何新的節點,只能調整樹中節點指針的指向。
在這裏插入圖片描述
轉化後變爲:
在這裏插入圖片描述

思路:先中序遍歷,再用2個for循環擺好指針的位置。

對二叉搜索樹進行中序遍歷後,就相當於排序了。

/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* left;
    Node* right;

    Node() {}

    Node(int _val) {
        val = _val;
        left = NULL;
        right = NULL;
    }

    Node(int _val, Node* _left, Node* _right) {
        val = _val;
        left = _left;
        right = _right;
    }
};
*/
class Solution {
public:
    Node* treeToDoublyList(Node* pRoot) {
        if (nullptr == pRoot){
            return nullptr;
        }
        tranverse(pRoot);
        return adjustTree();
    }

    vector<Node*> nodes;
    void tranverse(Node* pRoot) {//中序遍歷
        if (nullptr == pRoot)
            return;
        tranverse(pRoot->left);
        nodes.push_back(pRoot);
        tranverse(pRoot->right);
    }
     
    Node* adjustTree() {
        for (int i = 0; i < nodes.size() - 1; ++i)
            nodes[i]->right = nodes[i+1];     //因爲一個結點只有左指針和右指針而沒有next指針。
        nodes[nodes.size()-1]->right = nodes[0];//最後一個節點的右指向第一個節點
        //nodes[nodes.size()-1]->right = nullptr; //如果題目只要求轉化爲雙向鏈表,而不是循環雙向鏈表
        for (int i = nodes.size() - 1; i > 0; --i)
            nodes[i]->left = nodes[i-1];
        nodes[0]->left =nodes[nodes.size()-1] ;//第一個節點左指向租後一個節點
        //nodes[0]->left = nullptr; //如果題目只要求轉化爲雙向鏈表,而不是循環雙向鏈表
        return nodes[0];//返回頭節點
    }
};

ღღღ

(19)劍指33. 二叉搜索樹的後序遍歷序列【中等】

輸入一個整數數組,判斷該數組是不是某二叉搜索樹的後序遍歷結果。如果是則返回 true,否則返回 false。假設輸入的數組的任意兩個數字都互不相同。

參考以下這顆二叉搜索樹:
     5
    / \
   2   6
  / \
 1   3
示例 1:
輸入: [1,6,3,2,5]
輸出: false
示例 2:
輸入: [1,3,2,6,5]
輸出: true

思路:解題思路

後序遍歷最後節點爲根節點
二叉搜索樹左子樹都小於根節點,右子樹都大於根節點
因此可以找出左子樹和右子樹理論分界處進行判斷,再左右子樹遞歸判斷是否爲二叉搜索樹

/*
後序遍歷序列規律:最後一個元素爲根結點,前面左半部分爲左子樹,右半部分爲右子樹
BST規律:對任意結點,左結點<根結點<右結點的
根據這兩個規律進行判斷
*/
class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.empty()) return false;
        return verify(sequence, 0, sequence.size()-1);
    }
private:
    bool verify(vector<int>& a, int begin, int end){
        if(begin >= end) return true; //不理解爲什麼>= 
        							//只有一個元素了,其沒有左右子樹,沒有判斷的必要,故返回true
        int root = a[end]; //根結點的值
        //計算左子樹序列的長度(左子樹中結點值均小於根結點值)
        int i = begin;
        for(; i<end; i++){//注意這裏最多掃描到倒數第二個元素(最後一個元素爲根結點)
            if(a[i] > root) break; //一旦大於根結點就退出
        }//退出時,i即爲左子樹序列的長度
        //判斷右子樹序列是否滿足大於根結點的規律
        for(int j = i; j<end; j++){
            if(a[j] < root) return false; //若右子樹不滿足規律,則返回false
        }
        //判斷左子樹和右子樹是否爲二叉搜索樹
        return verify(a, begin, i-1) && verify(a, i, end-1);
    }
};

同上

class Solution {
public:
    bool verifyPostorder(vector<int>& postorder) {
        if(postorder.size() ==0) return true;
        return isPostorder(postorder, 0, postorder.size()-1);
    }

    //後序遍歷最後節點爲根節點,二叉搜索樹左子樹都小於根節點,右子樹都大於根節點,因此可以找出左子樹和右子樹理論分界處進行判斷,再左右子樹遞歸判斷
    bool isPostorder(vector<int>& postorder, int start, int end){
        if(start >= end) return true;
        int i=start;
        //while(postorder[i] < postorder[end])    //左子樹
        //    i++;
        for(; i<end;i++){ //注意:end已經是下標了,所以不是end-1
            if(postorder[i]>postorder[end]) break;
        }

        int bound = i;      //左右子樹理論分界點
        for(; i<end; i++)   //右子樹應該小於根節點,否則不是二叉搜索樹
        {
            if(postorder[i] < postorder[end]) return false;
        }
        return isPostorder(postorder, start, bound-1) && isPostorder(postorder, bound, end-1); //遞歸判斷左右子樹
    }
};
書上的方法,太複雜!
class Solution {
public:
    bool VerifySquenceOfBST(vector<int> sequence) {
        if(sequence.empty())
           return false;
        int length=sequence.size();
        int root=sequence[length-1];
        //找左子樹
        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;
        vector<int> sequence1;
        for(int m=0;m<i;++m)
            {
            sequence1.push_back(sequence[m]);
        }
        if(i>0)
            left=VerifySquenceOfBST(sequence1);
       //判斷右子樹是不是二叉搜索樹
        bool right=true;
        vector<int> sequence2;
        for(int m=0;m<length-i-1;++m)
            {
            sequence2.push_back(sequence[m+i]);
        }
        if(i<length-1)
            right=VerifySquenceOfBST(sequence2);
         
        return (left&&right);
    }
};

ღღღ

(20)劍指54. 二叉搜索樹的第k大節點

給定一棵二叉搜索樹,請找出其中第k大的節點。

示例 1:
輸入: root = [3,1,4,null,2], k = 1
   3
  / \
 1   4
  \
   2
輸出: 4
示例 2:
輸入: root = [5,3,6,2,4,null,null,1], k = 3
       5
      / \
     3   6
    / \
   2   4
  /
 1
輸出: 4

第k大節點,意思是從大的開始數,所以中序遍歷完之後從後面開始數。具體看題目示例吧。

注意函數要求的是節點本身,還是節點的值。對應中序遍歷 vec.push_back(root); 還是vec.push_back(root->val);

思路:中序遍歷完之後直接從後面開始取值。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int kthLargest(TreeNode* root, int k) { 
        if(root==NULL || k<=0) return NULL;
        vector<int> vec;
        inorder(root, vec);
        if(k>vec.size())  return NULL;
        return vec[vec.size()-k];
        //reverse(vec.begin(),vec.end());
        //return vec[k-1]; 
    }
    void inorder(TreeNode* root , vector<int>& vec){
        if(root==NULL) return;
        inorder(root->left,vec);
        vec.push_back(root->val);
        inorder(root->right,vec);
    }
};

ღღღ

(21)230. 二叉搜索樹中第K小的元素【中等】

方法一:中序遍歷遞歸法
BST中序遍歷之後爲從小到大排列
中序遍歷遞歸法,不是最優的,因爲是遍歷完之後在給出的結果(可用迭代法進行改進)
注:可在遞歸中加入判斷進行減枝

class Solution {
public:
    int kthSmallest(TreeNode* pRoot, int k) {
        if(pRoot==NULL||k<=0) return NULL;
        vector<int> vec;
        Inorder(pRoot,vec);
        if(k>vec.size())
            return NULL;
        return vec[k-1];
    }
       //中序遍歷,將節點依次壓入vector中
    void Inorder(TreeNode* pRoot,vector<int>& vec)
    {
        if(pRoot==NULL) return;
        Inorder(pRoot->left,vec);
        vec.push_back(pRoot->val);
        Inorder(pRoot->right,vec);
    } 
};

方法二:中序遍歷迭代法
在遍歷的過程中統計數量

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    int kthSmallest(TreeNode* root, int k) {
        int n=0;
		stack<TreeNode*> s;
		TreeNode *p = root;
		while(p || !s.empty()){
			if(p){ //當左結點不爲空時
				s.push(p); //入棧
				p = p->left; //指向下一個左結點
			}
			else{  //當左結點爲空時
				p = s.top();
				n++;  //統計數目(遍歷到了要訪問的父結點)
                if(n==k) return p->val;
				s.pop();         //出棧
				
				p = p->right;    //指向右結點
			}
		}
		return 0;		
    } 
};

ღღღ

(22)劍指68 - I. 二叉搜索樹的最近公共祖先(LC235)

給定一個二叉搜索樹, 找到該樹中兩個指定節點的最近公共祖先。
例如,給定如下二叉搜索樹: root = [6,2,8,0,4,7,9,null,null,3,5]
在這裏插入圖片描述

示例 1:
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
輸出: 6 
解釋: 節點 2 和節點 8 的最近公共祖先是 6。
示例 2:
輸入: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
輸出: 2
解釋: 節點 2 和節點 4 的最近公共祖先是 2, 因爲根據定義最近公共祖先節點可以爲節點本身。

思路

如果根節點的值大於p和q之間的較大值,說明p和q都在左子樹中,那麼此時我們就進入根節點的左子節點繼續遞歸,如果根節點小於p和q之間的較小值,說明p和q都在右子樹中,那麼此時我們就進入根節點的右子節點繼續遞歸,如果都不是,則說明當前根節點就是最小共同父節點,直接返回即可

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root==NULL) return NULL;
        if(p->val > root->val && q->val > root->val){//如果p,q在當前結點右子樹,則對右子樹遍歷
            return lowestCommonAncestor(root->right,p,q);
        }
        else if(p->val < root->val && q->val < root->val){//如果p,q在當前結點左子樹,則對左子樹遍歷
            return lowestCommonAncestor(root->left,p,q);
        }
        else return root; //如果當前結點在p,q之間,則爲最低公共父結點
    }
};

ღღღ

(23)劍指68 - II. 二叉樹的最近公共祖先(LC236)

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        // LCA(Least Common Ancestor)問題
        if (root == NULL) {//當遍歷到葉結點後就會返回null
            return NULL;
        }
        if (root == p || root == q) {//直接返回公共祖先
            return root;
        }
        TreeNode* left = lowestCommonAncestor(root->left, p, q);//返回的結點進行保存,可能是null
        TreeNode* right = lowestCommonAncestor(root->right, p, q);
        if (left != NULL && right != NULL) {
            return root;
        } else if (left != NULL) {
            return left;
        } else if (right != NULL) {
            return right;
        }
        return NULL;
    }
};

手畫遞歸過程:
在這裏插入圖片描述
ღღღ

(24)108. 將有序數組轉換爲二叉搜索樹

深度優先搜索/遞歸/分治

1、固定一個left和right變量,取mid作爲root節點。

2、遞歸得到root的左子樹root的右子樹。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return sortedArrayToBST(nums, 0, nums.size() - 1);//注意索引從0開始
    }
    TreeNode* sortedArrayToBST(vector<int>& nums, int l, int r) {
        if (r < l) return NULL;//遞歸子函數的出口(不能取等號,因爲單個元素也要分配空間)
        //int mid = l + (r-l)/2;
        int mid=(l+r)>>1;
        TreeNode* root = new TreeNode(nums[mid]); //構建根結點
        root->left = sortedArrayToBST(nums, l, mid - 1);//構建左子樹
        root->right = sortedArrayToBST(nums, mid + 1, r);//構建右子樹
        return root;//遞歸原始母函數的出口,返回最頂層的根結點指針
    }
};
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章