目錄
二十四 二叉樹中和為某一值的路徑3/2
二十六 二叉搜尋樹與雙向連結清單
三十八 二叉樹的深度
三十九 平衡二叉樹
五十八 二叉樹的下一個節點
對稱的二叉樹
二叉搜尋樹的第k個節點
按之字順序列印二叉樹3/3
二叉樹列印成多行
序列化二叉樹
二十四 二叉樹中和為某一值的路徑3/2
參考:https://www.cnblogs.com/silentteller/p/10829084.html
參考:https://www.cnblogs.com/silentteller/p/11925160.html
參考:https://www.cnblogs.com/wanglei5205/p/8686863.html
【前序周遊】【全局變量】
三個連結裡分别增加了全局變量,使得函數參數減少,一定程度上減少了麻煩。
思路:
1 使用二維向量res存儲全部路徑,使用一維向量s存儲目前路徑。
2 周遊二叉樹的過程:按前序周遊順序通路每一個節點。通路每個結點時,将結點添加到路徑向量tmp中。如果目前結點是葉子結點,則判斷目前路徑是否是符合條件的路徑,符合條件的路徑存入到二維向量res;如果目前結點不是葉子結點,則遞歸目前節點的左右子節點。
/*
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) {
if(root) dfs(root, expectNumber);
else return res;
}
void dfs(TrrNode * root, int sum){
s.push_back(root);//先序周遊,通路每個結點時,将結點添加到路徑向量s。
if(!(root -> left) && !(root -> right)){//是葉節點
if(sum - root -> val == 0)//則判斷目前路徑是否是符合條件的路徑,
res.push_back(s);//符合條件的路徑存入到二維向量res
}else{//不是葉節點,分别遞歸左右子樹
if(root -> left) dfs(root -> left, sum - root -> val);
if(root -> right) dfs(root -> right, sum - root -> val);
}
if(!s.empty())//s非空
s.pop_back();傳回父節點之前需要删除目前節點
}
private:
vector<vector<int>> res;//二維向量res存儲全部路徑
vector<int> s;//一維向量s存儲目前路徑
};
二十六 二叉搜尋樹與雙向連結清單
參考:https://www.cnblogs.com/silentteller/p/11942608.html
【中序周遊】【雙向連結清單】
class Solution {
public:
TreeNode* Convert(TreeNode* pRootOfTree)
{
if(pRootOfTree == nullptr)
return nullptr;//空樹
if(pRootOfTree -> left == nullptr && pRootOfTree -> right == nullptr)
return pRootOfTree;//隻有一個元素
helper(pRootOfTree);//完成中序周遊
for(int i = 0; i < node.size() - 1; i++){//按順序連接配接所有節點,循環到size-1節點停下
node[i] -> right = node[i + 1];//前後互連 node0為頭結點
node[i + 1] -> left = node[i];//【易錯】【雙向連結清單的連接配接】
}
return node[0];//傳回連結清單頭結點
}
void helper(TreeNode* root){
if(root == nullptr)
return;
helper(root -> left);//中序周遊
node.push_back(root);//node作為私有變量,存儲中序周遊結果
helper(root -> right);
}
private:
vector<TreeNode*> ndoe;//一維向量存儲節點
};
三十八 二叉樹的深度
參考:https://www.cnblogs.com/silentteller/p/12046692.html
【深度】
深度 = max(左子樹高度, 右子樹高度)+1;
遞歸直到葉節點,停止條件:葉節點->left == nullptr, return 0;
class Solution {
public:
int TreeDepth(TreeNode* pRoot)
{
if(pRoot == nullptr) return 0;
else return THeight(pRoot);
}
int THeight(TreeNode* root){
if(root == nullptr)
return 0;
else
return max(THeight(root -> left) + 1,THeight(root -> right) + 1);//+1放在max()的裡面
}
};
三十九 平衡二叉樹
參考:https://www.cnblogs.com/wanglei5205/p/8918624.html
參考:https://blog.csdn.net/dawn_after_dark/article/details/81702988
參考:https://www.cnblogs.com/silentteller/p/12051453.html
【二叉樹高度】
class Solution {
public:
bool IsBalanced_Solution(TreeNode* pRoot) {
//平衡二叉樹:abs(左子樹高度 - 右子樹高度)<=1
//計算二叉樹高度:遞歸找到葉節點
//-1表示false
if(getHeight(pRoot) == -1)
return false;
else
return true;
}
int getHeight(TreeNode* root){
if(root == nullptr)
return 0;
int LeftHeight = getHeight(root -> left);
if(LeftHeight == -1) return -1;
int RightHeight = getHeight(root -> right);
if(RightHeight == -1) return -1;
return abs(LeftHeight - RightHeight) <= 1? max(LeftHeight + 1, RightHeight + 1): -1;
}
};
五十八 二叉樹的下一個節點
參考:https://www.cnblogs.com/silentteller/p/12114894.html
二叉樹的中序周遊是左根右,是以如果一個結點的右子樹不為空,那麼這個節點的下一個節點一定是右子樹的最左結點,如果右子樹不存在左子樹的話,就傳回右子樹的根結點。
如果一個結點的右子樹為空,且目前結點是其父結點的左子樹的根結點,那麼直接傳回其父節點,否則就一直通路其父結點,直到找到一個結點是其父結點的左子結點,同樣傳回其父結點。
【中序周遊】【父指針】【邏輯】
這題一定要畫樹
class Solution {
public:
TreeLinkNode* GetNext(TreeLinkNode* pNode)
{//中序周遊下一個節點是右兒子
if(pNode == nullptr) return pNode;
if(pNode->right != nullptr){//節點右兒子存在
pNode = pNode -> right;//指針移到右兒子上
while(pNode -> left != nullptr)//找到右子樹中最左邊(最小)節點
pNode = pNode -> left;
return pNode;
}
while(pNode -> next != nullptr && pNode -> next->left != pNode){
pNode = pNode -> next;//右子樹不存在,一直向上找爸爸,直到找到一個爸爸是爺爺的左兒子。當找到根節點時候,他沒有父指針,是以傳回NULL
}
return pNode->next;//傳回父節點
}
};
對稱的二叉樹
參考:https://www.cnblogs.com/silentteller/p/12114934.html
與鏡像的差別,鏡像是将樹左右子樹一直交換,交換的元素值可以不同
對稱是左右兩邊必須是相同的數字
class Solution {
public:
bool isSymmetrical(TreeNode* pRoot)
{
if(pRoot == nullptr)
return true;//空樹對稱
return isDuichen(pRoot->left, pRoot -> right);
}
bool isDuichen(TreeNode* p1, TreeNode* p2){
if(p1 == nullptr && p2 == nullptr)
return true;//出口A 比較到左右樹都空了
if(p1 == nullptr && p2 != nullptr)
return false;//出口B
if(p2 == nullptr && p1 != nullptr)
return false;//出口C 一個空一個不空
if(p1->val != p2->val)
return false;//兩者非空,但值不相同
return isDuichen(p1->left,p2->right)&&isDuichen(p1->right,p2->left);
//【注意】比較的是最左和最右,中間兩個互相比較;ABCD比較AD和BC
}
};
二叉搜尋樹的第k個節點
參考:https://www.cnblogs.com/silentteller/p/12118658.html
二叉搜尋樹的中序周遊結果正好是按數值升序排列的結果,我們可以利用中序周遊将結點按val值的順序存儲到數組中,然後直接按索引傳回即可。
【中序周遊】
class Solution {
public:
TreeNode* KthNode(TreeNode* pRoot, int k)
{
if(pRoot == nullptr) return nullptr;
LDR(pRoot);
if(k>res.size() || k<1)
return nullptr;
return res[k - 1];
}
void LDR(TreeNode* root){
if(root == nullptr)
return;
LDR(root->left);
res.push_back(root);
LDR(root->right);
}
private:
vector<TreeNode*> res;
};
按之字順序列印二叉樹3/3
參考:https://www.cnblogs.com/silentteller/p/12115122.html
“實際上是二叉樹的層次周遊,隻不過每一行輸出的方向都是相反的,每周遊二叉樹的結點時,都将其内的每一個結點的左右孩子都儲存好,設定一個标志,ture時就正序輸出,false就逆序輸出val,直到所有的結點都列印完即可。”
參考:https://yq.aliyun.com/articles/368042
思路:【層序周遊】+搞一個flag來标志是正向還是負向列印,列印完一行取反
層序周遊一般方法:隊列,node出隊,node左右兒子入隊
本題思路:
A是列印行,B是列印行的兒子,考慮輸出方向,C作為輸出行;res(全局變量)儲存所有結果,儲存C
1 根節點壓入A,
開始while循環,循環條件 A非空
2 A非空,将A中元素a的左右兒子bc壓入B
3判斷flag,真:A從左往右掃描輸出(for循環 O(n))
假:A從右往左掃描輸出
掃描輸出結果存入C
4 flag取反
5 将C壓入res
6 交換AB,清空B
最終調出循環條件是:A中節點 == NULL
即通路到葉節點的兒子那一層,則跳出while循環
class Solution {
public:
vector<vector<int> > Print(TreeNode* pRoot) {
if(pRoot == nullptr) return res;
vector<TreeNode*> A;
vector<TreeNode*> B;
bool flag = true;
A.push_back(pRoot);
while(!A.empty()){
for(auto i:A){//【錯了一次】【掃描A中元素的左右兒子】
if(i->left != nullptr) B.push_back(i->left);
if(i->right != nullptr) B.push_back(i->right);
}
vector<int> C;
if(flag){
//從左往右輸出
for(int i = 0; i < A.size(); i++){
C.push_back(A[i]->val);//C是int類型,要壓入數字,不要忘記->val
}
}else{
//從右往左輸出
for(int i = A.size() - 1; i >= 0; i--)
C.push_back(A[i]->val);
}
flag = !flag;
res.push_back(C);
A.swap(B);
B.clear();
}
return res;
}
private:
vector<vector<int>> res;
};
二叉樹列印成多行
【層次周遊】
和上一題差不多,不需要flag和C
A是列印行,B是列印行的兒子;res儲存所有結果,儲存A
1 根節點壓入A,
開始while循環,循環條件 A非空
2 A非空,将A中元素a的左右兒子bc壓入B
3 A從左往右掃描輸出(for循環 O(n))
輸出結果存入C
4 将C壓入res
5 交換AB,清空B
最終調出循環條件是:A中節點 == NULL
即通路到葉節點的兒子那一層,則跳出while循環
/*
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> > Print(TreeNode* pRoot) {
if(pRoot == nullptr)
return res;
vector<TreeNode*> A,B;
A.push_back(pRoot);
while(!A.empty()){
for(auto i:A){
if(i->left) B.push_back(i->left);
if(i->right) B.push_back(i->right);
}
vector<int> C;
for(int i = 0; i < A.size(); i++){
C.push_back(A[i]->val);
}
res.push_back(C);
A.swap(B);
B.clear();
}
return res;
}
private:
vector<vector<int>> res;
};
序列化二叉樹
參考:https://blog.csdn.net/mbskypan/article/details/89676923?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task
參考:https://www.cnblogs.com/silentteller/p/12115563.html
思路:
序列化:把樹列印成一串字元
反序列化:把一串字元,重建成二叉樹
簡單地來看就是列印樹和重建樹
對于序列化:
a:全局變量,int型一維向量
res:儲存結果,int型數組
1 根節點是否為空,是->a中壓入‘#’字元
2根節點非空,對二叉樹進行前序周遊,将a中元素複制到res;
res為動态數組,根據a的大小建立
3 res強制轉換為字元型,傳回res
對于反序列化:
1,将輸入字元串數組 str 強制轉換成int類型數組 p
2,處理p
怎麼處理呢?
1 檢查p第一個元素是否為#,是->指針後移,傳回null,退出遞歸;【這裡的指針後移,是指到了左/右子樹的葉節點,要繼續移動指針,通路另一個子樹】
2 否-> 建立一個樹節點res,後移指針,樹的左兒子 = 遞歸處理;樹的右兒子 = 遞歸處理;
3傳回
/*
struct TreeNode {
int val;
struct TreeNode *left;
struct TreeNode *right;
TreeNode(int x) :
val(x), left(NULL), right(NULL) {
}
};
*/
class Solution {
public:
char* Serialize(TreeNode *root) {//傳回字元型數組類型
Dlr(root);對二叉樹進行前序周遊将節點加入數組
//将vector數組a中的元素一一複制到動态整型數組res中
int *res = new int[a.size()];
for(int i = 0;i < a.size(); i++)
res[i] = a[i];//複制【易錯:忘記複制了】
return (char*)res;//傳回字元型數組類型
}
TreeNode* Deserialize(char *str) {
int *p = (int*) str;//強制轉換,先将字元數組類型轉為整型數組
return helper(p);//二叉樹的節點的值就可以直接從整型數組中得到
}
TreeNode* helper(int *&str) {//【易錯:&對p的引用改變p的值】
if(*str == 0xFFFFFFFF){//處理遞歸出口,如果遇到0xFFFFFFF,
str++;//則指針向後移動一個機關
return nullptr;//并退出遞歸【易錯:傳回類型寫錯了】
}
//否則
TreeNode* res = new TreeNode(*str);//構造二叉樹根節點【易錯:竟然寫成了str,str是下标啊】
str++;//繼續移動指針
res->left = helper(str);
res->right = helper(str);//連接配接遞歸得到的左子樹以及右子樹
return res;//傳回二叉樹根節點【忘記傳回了】
}
//前序周遊
void Dlr(TreeNode *root){
if(root == nullptr){//當節點為空的時候,
a.push_back(0xFFFFFFFF);//加入特殊數值0xFFFFFFF,
return;//并退出遞歸【結束循環,傳回空】
}
a.push_back(root->val);//【壓入的是節點的值,要厘清楚壓入的是啥】
Dlr(root->left);
Dlr(root->right);
}
private:
vector<int> a;
};