您现在的位置是:首页 >学无止境 >算法Day17 | 110.平衡二叉树, 257. 二叉树的所有路径,404.左叶子之和网站首页学无止境
算法Day17 | 110.平衡二叉树, 257. 二叉树的所有路径,404.左叶子之和
简介算法Day17 | 110.平衡二叉树, 257. 二叉树的所有路径,404.左叶子之和
110.平衡二叉树
题目链接:110.平衡二叉树
求高度,后序遍历。
递归 后序遍历
如果某一节点为非平衡二叉树,则整个二叉树就为非平衡二叉树,可以一直向上层递归-1
class Solution {
public:
int recursion(TreeNode* cur) {
if (!cur) return 0;//终止条件
//左
int left = recursion(cur->left);
if (left == -1) return -1;//剪枝
//右
int right = recursion(cur->right);
if (right == -1) return -1;//剪枝
//中
return (abs(left - right) > 1) ? -1 : 1 + max(left, right);
}
bool isBalanced(TreeNode* root) {
return recursion(root) != -1;
}
};
迭代
层序遍历可以求深度(从上往下),但是不能求高度(从下往上)。
层序遍历各个节点,各个节点高度是通过后序遍历求出
class Solution {
public:
int getHeight(TreeNode* cur) {
stack<TreeNode*> st;
if (cur) st.push(cur);
int res = 0, height = 0;
while (!st.empty()) {
TreeNode* tmp = st.top();
st.pop();
if (tmp) {
st.push(tmp); st.push(nullptr);//中
++height;
if(tmp->right) st.push(tmp->right);//右
if(tmp->left) st.push(tmp->left);//左
} else {
st.pop();//出栈
--height;//回溯
}
res = res > height ? res : height;
}
return res;
}
bool isBalanced(TreeNode* root) {
stack<TreeNode*> st;
if (!root) return true;
else st.push(root);
while (!st.empty()) {
TreeNode* cur = st.top();
st.pop();
if (abs(getHeight(cur->left) - getHeight(cur->right)) > 1)
return false;
if (cur->right) st.push(cur->right);
if (cur->left) st.push(cur->left);
}
return true;
}
};
回溯法其实就是递归,但是很少人用迭代的方式去实现回溯算法!
因为对于回溯算法已经是非常复杂的递归了,如果再用迭代的话,就是自己给自己找麻烦,效率也并不一定高。
257. 二叉树的所有路径
题目链接: 257. 二叉树的所有路径
需要用深度遍历来求路径。深度遍历中,使用前序遍历。因为前序遍历的顺序为中左右,先遍历中间节点,再遍历子节点。
因为返回的为走过的路径。当找到第一条路径之后,寻找第二条路径,需要清除第一步路径走过的,直至清除剩余根节点。所以要回溯。
递归 前序遍历 回溯
class Solution {
public:
void traversal(TreeNode* cur, vector<int>& path, vector<string>& res) {
path.push_back(cur->val);//中
if (!cur->left && !cur->right) {//终止条件
string tmp;
//处理前size-1个元素,留下最后一个元素。因为最后一个元素没有"->"
for (int i = 0; i < path.size() - 1; ++i) {
tmp += to_string(path[i]);
tmp += "->";
}
res.push_back(tmp + to_string(path[path.size() - 1]));//加上最后一个元素
return;
}
if (cur->left) {//左
traversal(cur->left, path, res);
path.pop_back();//回溯
}
if (cur->right) {//右
traversal(cur->right, path, res);
path.pop_back();//回溯
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
vector<int> path;
traversal(root, path, res);
return res;
}
};
上述代码需要回溯,是因为每条路走完,最后的路径存储到path中,从path中提取int,再统一处理成string形式。
也可以每一步都处理string,而不是最后在统一处理成string,则不需要对path进行回溯。
递归 没有回溯
class Solution {
public:
void traversal(TreeNode* cur, string& path, vector<string>& res) {
path += to_string(cur->val);
if (!cur->left && !cur->right) {
res.push_back(path);
return;
}
if (cur->left){
string tmp = path + "->";
traversal(cur->left, tmp, res);
}
if (cur->right) {
string tmp = path + "->";
traversal(cur->right, tmp, res);
}
}
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
string path;
traversal(root, path, res);
return res;
}
};
下述代码中,string path
不能改为string& path
,因为传值时,只操作当前函数中path
,传址,则操作整个递归过程中的path
。
class Solution {
private:
void traversal(TreeNode* cur, string path, vector<string>& result) {
path += to_string(cur->val); // 中,中为什么写在这里,因为最后一个节点也要加入到path中
if (!cur->left && !cur->right) {
result.push_back(path);
return;
}
if (cur->left) {
path += "->";
traversal(cur->left, path, result); // 左
path.pop_back(); // 回溯 '>'
path.pop_back(); // 回溯 '-'
}
if (cur->right) {
path += "->";
traversal(cur->right, path, result); // 右
path.pop_back(); // 回溯'>'
path.pop_back(); // 回溯 '-'
}
}
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
string path;
traversal(root, path, result);
return result;
}
};
前序遍历,需要多一个辅助stack来保存路径上的节点。为什么要用stack呢,保证与前序遍历相同的处理方式。
迭代 前序遍历
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> res;
stack<string> stS;//保存路径的节点
stack<TreeNode*> stT;
stT.push(root);
stS.push(to_string(root->val));
while (!stT.empty()) {
TreeNode* cur = stT.top();stT.pop();//中
string path = stS.top();stS.pop();
if (!cur->left && !cur->right) {
res.push_back(path);
}
if (cur->right) {//右
stT.push(cur->right);
stS.push(path + "->" + to_string(cur->right->val));
}
if (cur->left) {//左
stT.push(cur->left);
stS.push(path + "->" + to_string(cur->left->val));
}
}
return res;
}
};
404.左叶子之和
题目链接:404.左叶子之和
判断节点是否为叶子节点,判断该节点左右子节点是否为空。
如何判断节点是否为左叶子,则需要通过该节点的父节点来判断,对于父节点来说,左子节点的左右子节点为空。
递归 后序遍历
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (!root) return 0;
if (!root->left && !root->right) return 0;
int left = sumOfLeftLeaves(root->left);//左
//判断左节点是否为叶子节点
if (root->left/*左节点存在*/ && !root->left->left && !root->left->right/*左节点的左右节点不存在*/)
left = root->left->val;
int right = sumOfLeftLeaves(root->right);//右
return left + right;//中
}
};
迭代 前序遍历
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
stack<TreeNode*> st;
st.push(root);
int res = 0;
while (!st.empty()) {
TreeNode* cur = st.top();//中
st.pop();
if (cur->left && !cur->left->left && !cur->left->right)
res += cur->left->val;
if (cur->right) st.push(cur->right);//右
if (cur->left) st.push(cur->left);//左
}
return res;
}
};
风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。