BM33.二叉树的镜像
思路:
因为我们需要将二叉树镜像,意味着每个左右子树都会交换位置,如果我们从上到下对遍历到的节点交换位置,但是它们后面的节点无法跟着他们一起被交换,因此我们可以考虑自底向上对每两个相对位置的节点交换位置,这样往上各个子树也会被交换位置。
自底向上的遍历方式,我们可以采用后序递归的方法。
具体做法:
- step 1:先深度最左端的节点,遇到空树返回,处理最左端的两个子节点交换位置。
- step 2:然后进入右子树,继续按照先左后右再回中的方式访问。
- step 3:再返回到父问题,交换父问题两个子节点的值。
class Solution {
public:
TreeNode* Mirror(TreeNode* pRoot) {
//空树返回
if(pRoot == NULL)
return NULL;
//先递归子树
TreeNode* left = Mirror(pRoot->left);
TreeNode* right = Mirror(pRoot->right);
//交换
pRoot->left = right;
pRoot->right = left;
return pRoot;
}
};
BM34.判断是不是二叉搜索树
思路:
二叉搜索树的特性就是中序遍历是递增序。既然是判断是否是二叉搜索树,那我们可以使用中序递归遍历。只要之前的节点是二叉树搜索树,那么如果当前的节点小于上一个节点值那么就可以向下判断。*只不过在过程中我们要求反退出。比如一个链表1->2->3->4,只要for循环遍历如果中间有不是递增的直接返回false即可。
1 2 |
|
具体做法:
- step 1:首先递归到最左,初始化maxLeft与pre。
- step 2:然后往后遍历整棵树,依次连接pre与当前节点,并更新pre。
- step 3:左子树如果不是二叉搜索树返回false。
- step 4:判断当前节点是不是小于前置节点,更新前置节点。
- step 5:最后由右子树的后面节点决定。
#include <climits>
class Solution {
public:
long pre = INT_MIN;
bool isValidBST(TreeNode* root) {
// 终止条件
if(root == nullptr) return true;
//左根右 前序遍历
if(!isValidBST(root->left))
return false;
//本级任务
if(root->val <= pre)
return false;
pre = root->val;
return isValidBST(root->right);
}
};
BM35.判断是不是完全二叉树
方法:层次遍历(推荐使用)
知识点:队列
队列是一种仅支持在表尾进行插入操作、在表头进行删除操作的线性表,插入端称为队尾,删除端称为队首,因整体类似排队的队伍而得名。它满足先进先出的性质,元素入队即将新元素加在队列的尾,元素出队即将队首元素取出,它后一个作为新的队首。
思路:
对完全二叉树最重要的定义就是叶子节点只能出现在最下层和次下层,所以我们想到可以使用队列辅助进行层次遍历——从上到下遍历所有层,每层从左到右,只有次下层和最下层才有叶子节点,其他层出现叶子节点就意味着不是完全二叉树。
具体做法:
- step 1:先判断空树一定是完全二叉树。
- step 2:初始化一个队列辅助层次遍历,将根节点加入。
- step 3:逐渐从队列中弹出元素访问节点,如果遇到某个节点为空,进行标记,代表到了完全二叉树的最下层,若是后续还有访问,则说明提前出现了叶子节点,不符合完全二叉树的性质。
- step 4:否则,继续加入左右子节点进入队列排队,等待访问。
class Solution {
public:
bool isCompleteTree(TreeNode* root) {
//空树一定是完全二叉树
if(root == NULL)
return true;
queue<TreeNode*> q;
//根节点先访问
q.push(root);
//定义一个首次出现的标记位
bool flag = false;
//层次遍历
while(!q.empty()){
int sz = q.size();
for (int i = 0; i < sz; i++) {
TreeNode* cur = q.front();
q.pop();
//标记第一次遇到空节点
if (cur == NULL)
flag = true;
else{
//后续访问已经遇到空节点了,说明经过了叶子
if (flag) return false;
q.push(cur->left);
q.push(cur->right);
}
}
}
return true;
}
};
BM36.判断是不是平衡二叉树
方法一:自顶向下
思路:
平衡二叉树任意节点两边的子树深度相差绝对值不会超过1,且每个子树都满足这个条件,那我们可以对每个节点找到两边的深度以后:
1 2 3 4 5 6 7 8 |
|
判断是否两边相差绝对值超过1:
1 2 3 |
|
然后因为每个子树都满足这个条件,我们还需要遍历二叉树每个节点当成一棵子树进行判断,而对于每个每个节点判断后,其子节点就是子问题,因此可以用递归。
1 |
|
具体做法:
- step 1:第一个函数递归遍历二叉树所有节点。
- step 2:对于每个节点判断,调用第二个函数获取子树深度。
- step 3:第二个函数递归获取子树深度,只需要不断往子节点深度遍历,累加左右深度的较大值。
- step 4:根据深度判断该节点下的子树是否为平衡二叉树。
class Solution {
public:
//计算该子树深度函数
int deep(TreeNode* root){
//空节点深度为0
if(root == NULL)
return 0;
//递归算左右子树的深度
int left = deep(root->left);
int right = deep(root->right);
//子树最大深度加上自己
return (left > right) ? left + 1 : right + 1;
}
bool IsBalanced_Solution(TreeNode* pRoot) {
//空树为平衡二叉树
if(pRoot == NULL)
return true;
int left = deep(pRoot->left);
int right = deep(pRoot->right);
//左子树深度减去右子树相差绝对值大于1
if(left - right > 1 || left - right < -1){
return false;
}
//同时,左右子树还必须是平衡的
return IsBalanced_Solution(pRoot->left) && IsBalanced_Solution(pRoot->right);
}
};
方法二:自底向上
思路:
上述一个函数算深度,一个函数遍历所有节点的方法,用了两个递归,做了很多不必要的运算,这就是自顶向下的弊端。那我们可以考虑自底向上,因为其实我们判断某个节点是否符合平衡二叉树特性的时候,需要的不是左右子树的完整深度,而是深度差,只要深度差绝对值不超过1,就可以满足。因此在底部计算深度的同时,判断该子树是否为平衡二叉树,将是或否与深度差一起往上传就行。
具体做法:
- step 1:先判断空树,直接为平衡二叉树。
- step 2:递归进行边计算深度边判断是否平衡二叉树。
- step 3:递归计算当前节点左右子树的深度差,然后比较深度差是否符合平衡二叉树。
- step 4:每次递归都将深度结果往上传,遇到不符合平衡二叉树的,返回-1,而深度差我们取绝对值保证返回正数,因此最后的负数一定就是 不满足条件,这样就能做到边判断边计算深度了。
class Solution {
public:
//计算该子树深度
bool judge(TreeNode* root, int& depth){
//空节点深度为0
if(root == NULL){
depth = 0;
return true;
}
//准备计算左右子树的深度
int left = 0;
int right = 0;
if(judge(root->left, left) == false || judge(root->right, right) == false)
return false;
//左子树深度减去右子树相差绝对值大于1
if(left - right > 1 || left - right < -1)
return false;
//子树最大深度加上自己
depth = (left > right) ? left + 1 : right + 1;
//该节点满足要求
return true;
}
bool IsBalanced_Solution(TreeNode* pRoot) {
int depth = 0;
return judge(pRoot, depth);
}
};