第六章 二叉树part04
今日内容:
● 110.平衡二叉树
● 257. 二叉树的所有路径
● 404.左叶子之和
详细布置
迭代法,大家可以直接过,二刷有精力的时候 再去掌握迭代法。
110.平衡二叉树 (优先掌握递归)
【链接】(文章,视频,题目)
再一次涉及到,什么是高度,什么是深度,可以巩固一下。
题目链接/文章讲解/视频讲解:代码随想录
【第一想法与实现(困难)】
【看后想法】
-
深度与高度
-
深度,是从根节点
-
高度,是到叶子结点
-
联系:树的深度=树的高度=根节点的最大高度
-
-
要获取左右子树的高度,高度,后序遍历左右中
-
用-1高度做特殊标志位,表示子树已经不是高度平衡
-
迭代方法效率较低,不掌握
【实现困难】
【自写代码】
class Solution {
public:
// 后序遍历左右中,求高度
// 返回值应为高度,-1表示已经不是高度平衡
int GetHeight(TreeNode* node) {
// 结束条件
if (!node) {
return 0;
}
int left_height = GetHeight(node->right);
if (left_height == -1) {
return -1;
}
int right_height = GetHeight(node->left);
if (right_height == -1) {
return -1;
}
if (std::abs(left_height - right_height) > 1) {
return -1;
}
return 1 + std::max(left_height, right_height);
}
bool isBalanced(TreeNode* root) {
return GetHeight(root) == -1 ? false : true;
}
};
【收获与时长】45m
257. 二叉树的所有路径 (优先掌握递归)
【链接】(文章,视频,题目)
这是大家第一次接触到回溯的过程, 我在视频里重点讲解了 本题为什么要有回溯,已经回溯的过程。
如果对回溯 似懂非懂,没关系, 可以先有个印象。
题目链接/文章讲解/视频讲解:代码随想录
【第一想法与实现(困难)】
-
找出所有的叶子结点,连续回溯找到父亲的父亲,直到根节点
-
没想好用什么结构去存储父节点信息
【看后想法】
-
由于要从根节点到叶子结点的路径,因此选择前序遍历遍历,中左右
-
递归结束条件,找到叶子结点,需要将跟到叶子的路径存下来,因此考虑每层递归中,把从根节点到当前结点的路径作为一个参数,并且把需要返回的多个路径集合作为另一个参数。函数类型void即可
【实现困难】
【自写代码】
class Solution {
public:
void traversal(TreeNode* node, std::vector<int>* path_ptr, std::vector<std::string>* path_str_vec_ptr) {
// 中,先处理,因为叶子结点也要接入到路径中
path_ptr->emplace_back(node->val);
// 结束条件,自身非空,且叶子结点
// 注意不允许空结点调用traversal函数,在调用之前先判断树结点指针有效
if (node && !node->left && !node->right) {
std::string path_str;
if (!path_ptr->empty()) {
path_str += std::to_string(path_ptr->front());
}
for (int i = 1; i < path_ptr->size(); ++i) {
path_str += "->";
path_str += std::to_string(path_ptr->at(i));
}
path_str_vec_ptr->emplace_back(std::move(path_str));
}
// 单层逻辑,前序遍历中左右
if (node->left) {
traversal(node->left, path_ptr, path_str_vec_ptr);
path_ptr->pop_back(); // 回溯,遍历完左子树,要剔除左孩子
}
if (node->right) {
traversal(node->right, path_ptr, path_str_vec_ptr);
path_ptr->pop_back(); // 回溯,遍历完右子树,要剔除右孩子
}
}
vector<string> binaryTreePaths(TreeNode* root) {
std::vector<int> path;
std::vector<std::string> res;
if (!root) {
return res;
}
traversal(root, &path, &res);
return res;
}
};
class Solution {
public:
// 递归回溯,前序遍历,隐含回溯,C++语法简化
void TraversePath(TreeNode* node, std::string path, std::vector<std::string>* res_ptr) {
if (!node) {
std::cout << "empty node" << std::endl;
return;
}
// 注意路径字符串,是拷贝传参,下层的修改不影响上层原本的参数
// 中,对根节点做特殊处理
if (path.empty()) {
path += std::to_string(node->val);
} else {
path += "->";
path += std::to_string(node->val);
}
// 结束条件
if (!node->left && !node->right) {
res_ptr->emplace_back(path);
}
// 前序遍历处理单层逻辑,中左右
if (node->left) {
// 此处不需要回溯,因为path不会因为下一层的递归而改变
TraversePath(node->left, path, res_ptr);
}
if (node->right) {
TraversePath(node->right, path, res_ptr);
}
}
vector<string> binaryTreePaths(TreeNode* root) {
std::string path;
vector<string> res;
if (!root) {
return res;
}
TraversePath(root, path, &res);
return res;
}
};
迭代方法
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
// 迭代方法,前序遍历,同时维护两个栈,树栈和路径栈,树栈每一个结点,同时对应路径栈的一个元素,也即该结点的完整路径(从根结点)
std::vector<std::string> res;
std::stack<TreeNode*> tree_st;
std::stack<std::string> path_st;
if (root) {
tree_st.push(root);
path_st.push(std::to_string(root->val));
}
while (!tree_st.empty()) {
TreeNode* node = tree_st.top();
tree_st.pop();
std::string path = path_st.top();
path_st.pop();
// 中,处理,遇到叶子结点
if (!node->left && !node->right) {
res.emplace_back(path);
}
// 右左,访问
if (node->right) {
tree_st.push(node->right);
path_st.push(path + "->" + std::to_string(node->right->val));
}
if (node->left) {
tree_st.push(node->left);
path_st.push(path + "->" + std::to_string(node->left->val));
}
}
return res;
}
};
【收获与时长】1h
404.左叶子之和 (优先掌握递归)
【链接】(文章,视频,题目)
其实本题有点文字游戏,搞清楚什么是左叶子,剩下的就是二叉树的基本操作。
题目链接/文章讲解/视频讲解:代码随想录
【第一想法与实现(困难)】
- 递归方式解决,单层逻辑就是:左叶子和 = 自身结点有无左右叶子 + 左子树 + 右子树
【看后想法】
-
简化递归,合并函数,究竟是什么顺序感觉不太重要,可以说是前中后都行
-
注意,左叶子的定义,就决定了必须从父结点来判断,不能从自身来判断。左叶子=是左孩子,并且是叶子结点
【实现困难】
【自写代码】
class Solution {
public:
// 递归,简化代码,合并函数
int sumOfLeftLeaves(TreeNode* root) {
// 结束条件
if (!root) {
return 0;
}
int res = 0;
// 判断自身的左孩子是叶子
if (root->left && !root->left->left && !root->left->right) {
res += root->left->val;
}
res += sumOfLeftLeaves(root->left);
res += sumOfLeftLeaves(root->right);
return res;
}
};
class Solution {
public:
// 迭代方法,遍历过程中,每个节点判断自身的左孩子,是否左叶子
int sumOfLeftLeaves(TreeNode* root) {
int res = 0;
// 前序遍历,中左右,入栈右左,非空才入栈
std::stack<TreeNode*> st;
if (root) {
st.push(root);
}
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
// 中,判断自身的左孩子是不是叶子
if (node->left && !node->left->left && !node->left->right) {
res += node->left->val;
}
// 右
if (node->right) {
st.push(node->right);
}
// 左
if (node->left) {
st.push(node->left);
}
}
return res;
}
};
【收获与时长】1h