110 平衡二叉树
文档讲解:代码随想录 (programmercarl.com)
视频讲解:后序遍历求高度,高度判断是否平衡 | LeetCode:110.平衡二叉树_哔哩哔哩_bilibili
状态:递归法做出来了。建议再看一下原理。迭代法就是把写一个求高度的函数,遍历每个节点时,通过对比其左右子树的高度来判断是否平衡,没什么意思。
思路
求深度可以从上到下去查,所以需要前序遍历(中左右),而高度只能从下到上去查,所以只能后序遍历(左右中)。
有的同学一定疑惑,为什么104.二叉树的最大深度 (opens new window)中求的是二叉树的最大深度,也用的是后序遍历。那是因为代码的逻辑其实是求的根节点的高度,而根节点的高度就是这棵树的最大深度,所以才可以使用后序遍历。
递归
既然要求比较高度,必然是要后序遍历。
明确递归函数的参数和返回值
参数:当前传入节点。 返回值:以当前传入节点为根节点的树的高度。
那么如何标记左右子树是否差值大于1呢?如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。
// -1 表示已经不是平衡二叉树了,否则返回值是以该节点为根节点树的高度 int getHeight(TreeNode* node)
明确终止条件
递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0。
if (node == NULL) {return 0;}
明确单层递归的逻辑
如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。
分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。
int leftHeight = getHeight(node->left); // 左 if (leftHeight == -1) return -1; int rightHeight = getHeight(node->right); // 右 if (rightHeight == -1) return -1; int result; if (abs(leftHeight - rightHeight) > 1) { // 中 result = -1; } else { result = 1 + max(leftHeight, rightHeight); // 以当前节点为根节点的树的最大高度 } return result;
自己写的代码
class Solution {
public:
int getHeight(TreeNode* node){
if(node == nullptr) return 0;
int leftHeight = getHeight(node->left);
int rightHeight = getHeight(node->right);
if(leftHeight == -1) return -1;
if(rightHeight == -1) return -1;
if(leftHeight - rightHeight > 1 || rightHeight - leftHeight > 1){
return -1;
}
else{
return (max(leftHeight, rightHeight) + 1);
}
}
bool isBalanced(TreeNode* root) {
int height = getHeight(root);
if(height == -1) return false;
else return true;
}
};
257 二叉树的所有路径
文档讲解:代码随想录 (programmercarl.com)
视频讲解:递归中带着回溯,你感受到了没?| LeetCode:257. 二叉树的所有路径_哔哩哔哩_bilibili
状态:递归法能写出来,迭代法没想到。
思路
递归法
!不能把递归的出口条件设置为node==NULL,否则就会出现下图情况:指针到了元素2左子树时保留路径,即"1–>2",这是不对的。只有当某个节点都没有左右孩子时才能保留路径,而图中元素2还有右孩子。
所以递归的出口条件应该为node–>left == NULL && node->right == NULL。
因为路径的保留要从根节点到叶子节点逐一保留,需要“中左右”的顺序来实现,所以用前序遍历。前序遍历用形参来传值,而不用返回值
class Solution { public: //前序遍历 //这里保留路径之所以用vector<int>而不用string,是因为元素值可能为一位数也可能为两位数,回溯时需要从路径中取出元素值,用string就不好判断去掉一个字符还是两个字符了。 void preorderTraversal(TreeNode* node, vector<int>& path, vector<string>& res){ path.push_back(node->val); //到了叶子节点,把路径存储下来 if(node->left == nullptr && node->right == nullptr){ string sPath; for(int i = 0; i < path.size() - 1; ++i){ sPath += to_string(path[i]); sPath += "->"; } sPath += to_string(path[path.size() - 1]); res.push_back(sPath); } // !!!注:回溯是在左节点递归结束后回溯,右节点递归结束后也回溯。(两个if里) if(node->left){ preorderTraversal(node->left, path, res); path.pop_back();//!注意要回溯 } if(node->right){ preorderTraversal(node->right, path, res); path.pop_back();//!注意要回溯 } } vector<string> binaryTreePaths(TreeNode* root) { vector<int> path; vector<string> res; if(root == nullptr) return res; preorderTraversal(root, path, res); return res; } };
注意本题需要回溯,如下图,回溯和递归是一一对应的,有一个递归,就要有一个回溯
迭代法(前序遍历)
这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。路径栈和元素栈里的元素一一对应,个数相同,同时入栈出栈。当指针指向两个栈的同一位置时,路径栈的元素表示的是:从根节点到元素栈的元素的路径。
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
stack<TreeNode*> treeST;// 元素栈:保存树的遍历节点
stack<string> pathST;// 路径栈:保存遍历路径的节点
vector<string> result;// 保存最终路径集合
if(root == nullptr) return result;
treeST.push(root); pathST.push(to_string(root->val));
while(!treeST.empty()){
TreeNode* node = treeST.top(); treeST.pop();// 取出节点 中
string path = pathST.top(); pathST.pop();// 取出该节点对应的路径
if(node->left == nullptr && node->right == nullptr){// 遇到叶子节点
result.push_back(path);
}
if(node->right){// 右
treeST.push(node->right);
pathST.push(path + "->" + to_string(node->right->val));
}
if(node->left){// 左
treeST.push(node->left);
pathST.push(path + "->" + to_string(node->left->val));
}
}
return result;
}
};
小总结
本周小结!(二叉树系列二) | 代码随想录 (programmercarl.com)
404 左叶子之和
文档讲解:代码随想录 (programmercarl.com)
视频讲解:二叉树的题目中,总有一些规则让你找不到北 | LeetCode:404.左叶子之和_哔哩哔哩_bilibili
状态:递归法做出来了,不过不能保证下次能做出来。
思路
递归法
左叶子的明确定义:节点A的左孩子不为空,且左孩子的左右孩子都为空(说明是叶子节点),那么A节点的左孩子为左叶子节点
那么判断当前节点是不是左叶子是无法判断的,必须要通过节点的父节点来判断其左孩子是不是左叶子。 如果该节点的左节点不为空,该节点的左节点的左节点为空,该节点的左节点的右节点为空,则找到了一个左叶子,判断代码如下:
if (node->left != NULL && node->left->left == NULL && node->left->right == NULL) { 左叶子节点处理逻辑 }
递归的遍历顺序为后序遍历(左右中),是因为要通过递归函数的返回值来累加求取左叶子数值之和。
递归三部曲:
确定递归函数的参数和返回值
判断一个树的左叶子节点之和,那么一定要传入树的根节点,递归函数的返回值为数值之和,所以为int。使用题目中给出的函数就可以了。
确定终止条件
如果遍历到空节点,那么左叶子值一定是0
if (root == NULL) return 0;
注意,只有当前遍历的节点是父节点,才能判断其子节点是不是左叶子。 所以如果当前遍历的节点是叶子节点,那其左叶子也必定是0,那么终止条件为:
if (root == NULL) return 0; if (root->left == NULL && root->right== NULL) return 0; //其实这个也可以不写,如果不写不影响结果,但就会让递归多进行了一层。
确定单层递归的逻辑
当遇到左叶子节点的时候,记录数值,然后通过递归求取左子树左叶子之和,和 右子树左叶子之和,相加便是整个树的左叶子之和。代码如下:
int leftValue = sumOfLeftLeaves(root->left); // 左 if (root->left && !root->left->left && !root->left->right) { leftValue = root->left->val; } int rightValue = sumOfLeftLeaves(root->right); // 右 int sum = leftValue + rightValue; // 中 return sum;
整体递归代码如下
class Solution { public: int sumOfLeftLeaves(TreeNode* root) { if (root == NULL) return 0; if (root->left == NULL && root->right== NULL) return 0; int leftValue = sumOfLeftLeaves(root->left); // 左 if (root->left && !root->left->left && !root->left->right) { // 左子树就是一个左叶子的情况 leftValue = root->left->val; } int rightValue = sumOfLeftLeaves(root->right); // 右 int sum = leftValue + rightValue; // 中 return sum; } };
本人写的递归代码如下
class Solution { public: //后序遍历:因为要将返回值作为左叶子之和 int postorderTraversal(TreeNode* node){ if(node == nullptr) return 0; //叶结点,出口 int leftSum = postorderTraversal(node->left); //当前节点左子树的 左叶子之和 int rightSum = postorderTraversal(node->right); //当前节点右子树的 左叶子之和 //中:若当前节点的左孩子为叶子结点,则将该叶子节点的值 加到 该结点左子树的左叶子之和 if(node->left != nullptr && node->left->left == nullptr && node->left->right == nullptr){ leftSum += node->left->val; } // 当前节点的左叶子之和 = 当前节点左子树的 左叶子之和 + 当前节点右子树的 左叶子之和 return (leftSum + rightSum); } int sumOfLeftLeaves(TreeNode* root) { return postorderTraversal(root); } };
迭代法
前中后序、层序遍历都可以,只要把符合条件的节点加上就行。
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
stack<TreeNode*> st;
int result = 0;
if(root == nullptr) return result;
st.push(root);
while(!st.empty()){
TreeNode* node = st.top();
st.pop();
if(node->left != nullptr && node->left->left == nullptr && node->left->right == nullptr){
result += node->left->val;
}
if(node->left) st.push(node->left);
if(node->right) st.push(node->right);
}
return result;
}
};