程式碼隨想錄:二叉樹的屬性
二叉樹的深度
遞迴法獲得二叉樹的深度
樹的深度 -> 後序遍歷求根節點的高度
思路:遞迴三步驟。求根節點的高度,即最大深度,可以用後序遍歷的邏輯
1.確定函式形式,返回型別,引數:輸入一個TreeNode,返回一個int深度,so
int getMaxDepth(treeNode * node)
2. 確定終止條件: 如果node為空,就返回0,表示高度為0
if(node == NULL) return 0;
3. 確定遞迴邏輯:對一個節點而言,看左右子節點返回的高度,哪個高度大作為該節點的返回(+1是加了當前節點的高度1)
int leftDepth = getMaxDepth(node->left); int rightDepth = getMaxDepth(node->right); int maxDepth = 1 + max(leftDepth, rightDepth); return maxDepth;
整合程式碼
class Solution { public: int getdepth(TreeNode* root) { if (root == NULL) return 0; int leftdepth = getdepth(root->left); // 左 int rightdepth = getdepth(root->right); // 右 int depth = 1 + max(leftdepth, rightdepth); // 中 return depth; }int maxDepth(TreeNode* root) { return getdepth(root); } };
還可以精簡程式碼:
class solution { public: int maxdepth(treenode* root) { if (root == null) return 0; return 1 + max(maxdepth(root->left), maxdepth(root->right)); } };
前序遍歷求深度
實際上前序遍歷才能體現求深度的真正邏輯:
class Solution { public: int result; void getdepth(TreeNode* root, int depth){ //前序遍歷 中 左 右 result = depth > result?depth : result;//中 if(root->left==NULL &&root->right==NULL) return; if(root->left){//左 depth++; getdepth(root->left,depth); depth--;//回溯 } if(root->right){//右 depth++; getdepth(root->right,depth); depth--;//回溯 } return; } int maxDepth(TreeNode* root) { result = 0; if(root==NULL) return result; getdepth(root,1); return result; } }
上面有步驟可以簡化程式碼:
if(root->left) getDepth(node->left, depth + 1); if(root->right) getDepth(node->right, depth + 1);
迭代法獲得二叉樹的深度
可以用層序遍歷法來得到二叉樹的最大深度和最小深度
二叉樹的最大深度
104. 二叉樹的最大深度 - 力扣(LeetCode) (leetcode-cn.com)
給定一個二叉樹,找出其最大深度。
二叉樹的深度為根節點到最遠葉子節點的最長路徑上的節點數。
說明: 葉子節點是指沒有子節點的節點
思路:層序遍歷,當每遍歷一層,就把深度加1,當遍歷完最後一層,佇列為空,退出。
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int maxDepth(TreeNode* root) {
queue<TreeNode*> queueNode;
if(root != NULL) queueNode.push(root);
//空二叉樹拳頭警告
//vector<vector<int>> res;
int depth = 0;
while(!queueNode.empty()){
//每次遍歷一層,一層的節點在進入while時候確定,後面會改變,所以for迴圈中不能用size(),而應該用固定長度
int layerSize = queueNode.size();
for(int i =0;i<layerSize;i++){
TreeNode *tempNode = queueNode.front();
queueNode.pop();
if(tempNode->left) queueNode.push(tempNode->left);
if(tempNode->right) queueNode.push(tempNode->right);
}
depth+=1;
}
return depth;
}
};
二叉樹的最小深度
111. 二叉樹的最小深度 - 力扣(LeetCode) (leetcode-cn.com)
給定一個二叉樹,找出其最小深度。
最小深度是從根節點到最近葉子節點的最短路徑上的節點數量。
錯的思路:當某層節點數不為2^(k-1)時,就可能為最小深度
注意,只有當左右孩子都為空的時候,才說明遍歷的最低點了。如果其中一個孩子為空則不是最低點
所以只有當某個節點左右孩子都指向NULL,才作為最小深度的層的節點
/**
* Definition for a binary tree node.
* struct TreeNode {
* int val;
* TreeNode *left;
* TreeNode *right;
* TreeNode() : val(0), left(nullptr), right(nullptr) {}
* TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
* TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
* };
*/
class Solution {
public:
int minDepth(TreeNode* root) {
queue<TreeNode*> queueNode;
if(root != NULL) queueNode.push(root);
if(root == NULL) return 0;
//空二叉樹拳頭警告
//vector<vector<int>> res;
int minDepth = 1;
while(!queueNode.empty()){
//每次遍歷一層,一層的節點在進入while時候確定,後面會改變,所以for迴圈中不能用size(),而應該用固定長度
int layerSize = queueNode.size();
for(int i =0;i<layerSize;i++){
TreeNode *tempNode = queueNode.front();
queueNode.pop();
if(tempNode->left) queueNode.push(tempNode->left);
if(tempNode->right) queueNode.push(tempNode->right);
if(!tempNode->left && !tempNode->right) return minDepth;
}
minDepth+=1;
}
return minDepth;
}
};
平衡二叉樹
對稱二叉樹
101. 對稱二叉樹 - 力扣(LeetCode) (leetcode-cn.com)
也叫映象二叉樹,即判斷給定root的二叉樹是不是映象/對稱的。
對稱的二叉樹:
不對稱的二叉樹:
遞迴判斷對稱二叉樹
思路:遞迴。
關於中間對稱軸對稱的兩節點是否相同,而不僅僅是左右節點相同。so比較對稱的兩棵子樹是否是相互翻轉的。。
一個遞迴 左右中,一個遞迴 右左中,來比較對稱的子樹(左邊看和右邊看過去是一樣的)
既然是遞迴方法,遞迴三部曲:背(確定函式引數和返回值,確定終止條件,確定遞迴邏輯)
確定函式引數和返回值
這裡,要遞迴比較兩棵對稱的子樹是否是相互翻轉,引數為兩棵子樹的root,返回值是true 或者false.
bool cmp(TreeNode * left, TreeNode * right){ //遞迴函式 }
確定終止條件
什麼時候不對稱呢返回false?外側空節點不對稱,內測空節點不對稱,當前root的val不相等
什麼時候對稱呢返回true?左右節點都不為空,且數值相同的情況(p.s. 另外還有一種情況,左右子樹都為空,即到了葉子節點)
if(left == NULL && right != NULL) return false; else if (left != NULL && right == NULL) return false; else if (left->val != right->val) return false; else if (left == NULL && right == NULL) return true;
確定遞迴邏輯
處理左右子樹都不為空,且數值相同的情況。
要遞迴比較左子樹的外側和右子樹的外側 left->left vs right->right
要遞迴比較左子樹的內測和右子樹的內測 left->right vs right->left
只有當以上兩個比較都返回true時候,才返回true
bool outside = cmp(left->left, right->right); bool inside = cmp(left->right, right->left); return ouside&&inside;
完成程式碼:
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: bool cmp(TreeNode * left, TreeNode * right){ //遞迴函式 if(left == NULL && right != NULL) return false; else if (left != NULL && right == NULL) return false; else if (left == NULL && right == NULL) return true; else if (left->val != right->val) return false;//這一條要放到下面,不然可能會出現子節點為空就讀取val的錯誤 //左右對稱節點不空,val相等的情況 bool outside = cmp(left->left, right->right); bool inside = cmp(left->right, right->left); return outside && inside; } bool isSymmetric(TreeNode* root) { if(root==NULL) return true; return cmp(root->left, root->right); } };
“迭代邏輯”判斷對稱二叉樹
使用佇列來遍歷二叉樹,把對應的元素按特定的順序放入佇列。每次比較兩個節點,看看相對應的節點是否相等
class Solution { public: bool isSymmetric(TreeNode* root) { if(root==NULL) return true; //佇列存放比較節點 queue<TreeNode*> que; que.push(root->left); que.push(root->right); while(!que.empty()){ //把要比較的節點彈出來暫存 佇列先進先出 TreeNode * leftTemp = que.front(); que.pop(); TreeNode * rightTemp = que.front(); que.pop(); //當相對的兩個節點都為空,則區域性對稱 if(!leftTemp && !rightTemp) continue; //相對的兩個節點一空一有值,或者是都不為空但是值不相等 if(leftTemp&&!rightTemp || !leftTemp&&rightTemp || (leftTemp->val!=rightTemp->val)) return false; //因為上面兩個if排除了為有空節點的情況,只剩下兩個節點不為空,且值相等,所以push沒毛病 que.push(leftTemp->left); que.push(rightTemp->right); que.push(leftTemp->right); que.push(rightTemp->left); } return true; } };
上面程式碼其實是把左右兩個子樹要比較的元素順序放進一個容器,然後成對成對的取出來進行比較。那麼除了佇列,使用棧也可以啊,只要把容器替換一下,佇列改為棧(因為成對成對彈出壓入,不用改什麼順序)。
平衡二叉樹
110. 平衡二叉樹 - 力扣(LeetCode) (leetcode-cn.com)
給定一個二叉樹,判斷它是否是高度平衡的二叉樹。本題中,一棵高度平衡二叉樹定義為:
一個二叉樹每個節點 的左右兩個子樹的高度差的絕對值不超過 1 。
思路:重點是平衡怎麼判斷?左右子樹的高度差絕對值小於等於1,既然是比較高度,遞迴+後序遍歷就可以了:
遞迴函式的引數和返回值:返回的是當前節點的高度,引數為當前節點的指標
int getHeight(TreeNode * root){ //遞迴函式 }
終止條件:如果遇到空節點(葉子節點指向的NULL),即返回 0
if(root == NULL) return 0;
單層遞迴邏輯: 先遞迴讀取左子樹高度,再遞迴讀取右子樹高度,相比較返回大的,並且差值如果大於等於2就不是平衡二叉樹(但是因為是遞迴演算法,不是很好直接退出函式返回,使用一個flag:-1 如果某一次的高度差大於1,那就是不平衡,一直影響到最後的輸出)
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? -1 :max(leftHeight, rightHeight)+1;
寫出程式碼:
/** * Definition for a binary tree node. * struct TreeNode { * int val; * TreeNode *left; * TreeNode *right; * TreeNode() : val(0), left(nullptr), right(nullptr) {} * TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} * TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {} * }; */ class Solution { public: int getHeight(TreeNode* root){ if(root == NULL) return 0; int leftHeight = getHeight(root->left); if(leftHeight==-1) return -1; int rightHeight = getHeight(root->right); if(rightHeight==-1) return -1; return abs(rightHeight-leftHeight) > 1? -1:max(rightHeight,leftHeight)+1; } bool isBalanced(TreeNode* root) { int heigh = getHeight(root); if(heigh==-1) return false; else return true; } };