110.平衡二叉树
难度指数:😀😐
重点讲解如何通过后序遍历判断二叉树是否是平衡二叉树。
当我们发现任何一个节点,它的左右孩子不符合平衡二叉树的条件,那么整棵树就不是平衡二叉树。
递归解法:(后序)
代码思路:
int getheight(node)
{
if (node == NULL) {
return 0; //若一个节点下面的节点是空节点,那么高度就是0
}
leftheight = getheight(node->left); //左 统计左子树的高度
if (leftheight == -1) { //说明左子树本身不是一棵平衡二叉树
return -1;
}
rightheight = getheight(node->right); //右
if (rightheight == -1) {
return -1;
}
int result;
//中
if (abs(rightheight - leftheight) > 1) {
result = -1; //return -1; 也是可以的 表示不是平衡二叉树
}
else {
result = 1 + max(rightheight, leftheight);
}
return result;
}
Q:计算这个高度干啥?
A:计算这个高度,是为了计算高度差是否小于等于1,来判断是不是平衡二叉树。
代码并不复杂,关键在于对高度的理解。
求高度,为什么要用后序遍历?
可以看到在中比较高度(计算左右高度差),是由前面的左 向左递归,得到左子树的高度;以及右 向右递归得到右子树的高度。
中只有在左和右的下面才能进行两个高度的比较,然后将比较的结果返回给上一层节点,
所以前序遍历是不可以的!
最后返回给根节点的信息,就告诉我们这棵二叉树是不是平衡二叉树。
AC代码: (核心代码模式)
class Solution {
public:
int getHeight(TreeNode* node) {
if (node == NULL) {
return 0;
}
int leftHeight = getHeight(node->left); //左 统计左子树的高度
if (leftHeight == -1) { //说明左子树本身不是一棵平衡二叉树
return -1;
}
int rightHeight = getHeight(node->right); //右
if (rightHeight == -1) {
return -1;
}
int result; //存结果
//中
if (abs(rightHeight - leftHeight) > 1) {
result = -1; //return -1; 也可以(只不过不够清晰) 表示不是平衡二叉树
}
else {
result = 1 + max(rightHeight, leftHeight);
}
return result;
}
bool isBalanced(TreeNode* root) {
return getHeight(root) == -1 ? false : true;
}
};
迭代解法:
待补充
AC代码: (核心代码模式)
257.二叉树的所有路径
难度指数:😀😐
求路径,要使用前序遍历。
只有前序遍历,才能让父节点指向它的孩子节点,这样才能把路径按照要求的顺序输出。
牛角尖发问:中序、后序遍历写法就不行?
你可以试试,不可以!
递归解法:(前序)
说到递归,就不得不提回溯,本题是第一次正式提到回溯,其实回溯和递归是相辅相成的,只要有递归就一定有回溯。
那我前面写过的那么多题目,怎么没感到有回溯?其实无论你用没用回溯,回溯都在那里。回溯的过程就在递归函数的下面。
那这个题为什么会有回溯的过程?
一个容器收集路径,收集到了1->2->5,
那么,如何把5弹出去,把2弹出去,再重新收集到1->3,(把5弹出去,2弹出去,这个过程就是回溯的过程。)
代码思路:
传入一个根节点,传入一个数组(
path用来记录单条路径),传入一个数组(放最后结果,每个数组元素是字符串类型,放每条路径)
path定义的是一个vector数组的类型,而且参数列表里使用了引用,引用是地址拷贝。(这么做是为了方便展示回溯的过程)
(path 也可以定义成 string 类型,也可以不用引用,代码会简短,但是会把回溯的过程隐藏了。)
path 是数组,需要把数组转成 string 类型,同时每个元素之间需要加 ->
⚠️本题有点特殊,中要写在终止条件的上面。
如果把中写在终止条件之后,那么最后遍历到叶子节点的时候,叶子节点的值就没有放到path里面。
因为遍历到叶子节点,该路径就结束了,会导致这个中没有收集到叶子节点,
所以路径收集的节点要放在终止条件的上面,这样,最后的叶子节点也能放到path。
void traversal(TreeNode* node, vector<int>& path, vector<string>& result) {
path.push_back(node->val); //中
//终止条件
if (node->left == NULL && node->right == NULL) { //如果左、右孩子都为空,说明遍历到叶子节点了
result.push_back(path);
return;
}
//node就不怕是空节点吗? 如果是空节点,那这样就是空指针异常了
if (node->left) { //所以,向左遍历的时候会判断如果这个节点不为空,才会向左遍历
traversal(node->left, path, result); //递归 (有递归就有回溯)
path.pop_back(); //回溯
}
if (node->right) {
traversal(node->right, path, result);
path.pop_back();
}
}
代码看起来还是比较冗余的,主要为了体现回溯的过程,便于理解。
AC代码: (核心代码模式)
class Solution {
public:
void traversal(TreeNode* node, vector<int>& path, vector<string>& result) {
path.push_back(node->val); //中
//终止条件
if (node->left == NULL && node->right == NULL) {
string sPath;
for (int i = 0; i < path.size() - 1; i++) {
sPath += to_string(path[i]);
sPath += "->";
}
sPath += to_string(path[path.size() - 1]);
result.push_back(sPath);
return;
}
//左
if (node->left) {
traversal(node->left, path, result); //递归
path.pop_back(); //回溯
}
if (node->right) {
traversal(node->right, path, result);
path.pop_back(); //回溯
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
vector<int> path;
if (root == NULL) {
return result;
}
traversal(root, path, result);
return result;
}
};
思考:path和spath的区别是什么?
迭代解法:
待补充
404.左叶子之和
难度指数:😀😐
其左、右孩子为空,那么该节点一定是叶子节点。
左叶子节点:首先一定要是叶子节点,然后是其父节点的左孩子。
遍历到6时,我可以知道它是不是叶子节点,因为判断6的左右孩子均为空时,它就是叶子节点,
但是无法判断6是不是它的父节点的左孩子。
解决方法:我们选择只遍历到9,判断9的左孩子是否为空;如果左孩子不为空,同时左孩子的左、右孩子均为空,
那么当前遍历的节点9的左孩子就是我们要的左叶子节点。
用后序遍历比较好,因为需要一层一层向上返回,这样父节点可以得到左子树的左叶子之和,右子树的左叶子之和,相加。
一层一层往上,传给根节点。
递归解法:(后序)
代码思路:
直接使用力扣上给我们的主函数就可以
int traversal(TreeNode* node) {
//终止条件
if (node == NULL) { //如果这个节点是空
return 0; //左叶子之和一定是0
}
//如果遇到叶子节点
if (node->left == NULL && node->right == NULL) { //这步不写也没啥问题
return 0;
}
//左
int leftNum = traversal(node->left); //收集左子树中符合左叶子条件的所有数之和
if (node->left !=NULL && node->left->left == NULL && node->left->right == NULL) {
leftNum = node->left->val;
}
//右
int rightNum = traversal(node->right); //收集右子树中符合左叶子条件的所有数之和
//中
int sum = leftNum + rightNum;
return sum;
}
我们只有遍历到9的时候,才能把这个9收集起来,
所以遇到叶子节点的时候,也是return 0;
代码谈不上精简,有很大的精简空间。
不建议初学者看精简版的代码,
AC代码: (核心代码模式)
class Solution {
public:
int sumOfLeftLeaves(TreeNode* node) {
if (node == NULL) {
return 0;
}
//如果遇到叶子节点
if (node->left == NULL && node->right == NULL) {
return 0;
}
//左
int leftNum = sumOfLeftLeaves(node->left); //收集左子树中符合左叶子条件的所有数之和
if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) {
leftNum = node->left->val;
}
//右
int rightNum = sumOfLeftLeaves(node->right); //收集左子树中符合左叶子条件的所有数之和
//中
int sum = leftNum + rightNum;
return sum;
}
};
迭代解法:
待补充