<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;//遞歸原始母函數的出口,返回最頂層的根結點指針
}
};