110.平衡二叉树
题目链接:平衡二叉树
题目描述:给定一个二叉树,判断它是否是平衡二叉树
解题思路:
平衡二叉树是指一个二叉树每个节点的左右两个子树的高度差的绝对值不超过1,这里要注意是每个。
此时大家应该明白了既然要求比较高度,必然是要后序遍历。
递归三步曲分析:
-
明确递归函数的参数和返回值
参数:当前传入节点。 返回值:以当前传入节点为根节点的树的高度。
如果当前传入节点为根节点的二叉树已经不是二叉平衡树了,还返回高度的话就没有意义了。
所以如果已经不是二叉平衡树了,可以返回-1 来标记已经不符合平衡树的规则了。 -
明确终止条件
递归的过程中依然是遇到空节点了为终止,返回0,表示当前节点为根节点的树高度为0 -
明确单层递归的逻辑
如何判断以当前传入节点为根节点的二叉树是否是平衡二叉树呢?当然是其左子树高度和其右子树高度的差值。分别求出其左右子树的高度,然后如果差值小于等于1,则返回当前二叉树的高度,否则返回-1,表示已经不是二叉平衡树了。
class Solution {
public:
bool isBalanced(TreeNode* root) {
if (getHeight(root) == -1)
return false;
else
return true;
}
private:
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;
if (abs(leftHeight - rightHeight) > 1)
return -1;
else
return max(leftHeight, rightHeight) + 1;
}
};
本题的迭代方式可以先定义一个函数,专门用来求高度。这个函数通过层序遍历找每一个节点的高度(其实是通过求传入节点为根节点的最大深度来求的高度),然后再用栈来模拟前序遍历,遍历每一个节点的时候,再去判断左右孩子的高度是否符合。
class Solution {
public:
bool isBalanced(TreeNode* root) {
stack<TreeNode*> st;
if (root == NULL)
return true;
st.push(root);
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
if (abs(getDepth(node->left) - getDepth(node->right)) > 1) {
return false;
}
if (node->right)
st.push(node->right);
if (node->left)
st.push(node->left);
}
return true;
}
private:
int getDepth(TreeNode* root) {
if (root == NULL)
return 0;
int depth = 0;
queue<TreeNode*> que;
que.push(root);
while (!que.empty()) {
int size = que.size();
depth++;
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
que.pop();
if (node->left)
que.push(node->left);
if (node->right)
que.push(node->right);
}
}
return depth;
}
};
257. 二叉树的所有路径
题目链接:二叉树的所有路径
题目描述:给你一个二叉树的根节点
root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。叶子节点 是指没有子节点的节点。
解题思路:
这道题目要求从根节点到叶子的路径,所以需要前序遍历,这样才方便让父节点指向孩子节点,找到对应的路径。在这道题目中将第一次涉及到回溯,因为我们要把路径记录下来,需要回溯来回退一个路径再进入另一个路径。
前序遍历以及回溯的过程如图:
递归法:
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<int> path;
vector<string> result;
traversal(root,path,result);
return result;
}
private:
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();
}
}
};
精简版,这里在函数定义的时候void traversal(TreeNode* cur, string path, vector<string>& result)
,定义的是string path
,每次都是复制赋值,不用使用引用,否则就无法做到回溯的效果。那么在如上代码中,貌似没有看到回溯的逻辑,其实不然,回溯就隐藏在traversal(cur->left, path + "->", result);
中的 path + "->"
。 每次函数调用完,path依然是没有加上"->" 的,这就是回溯了。
class Solution {
private:
void traversal(TreeNode* cur, string path, vector<string>& result) {
path += to_string(cur->val); // 中
if (cur->left == NULL && cur->right == NULL) {
result.push_back(path);
return;
}
if (cur->left) traversal(cur->left, path + "->", result); // 左
if (cur->right) traversal(cur->right, path + "->", result); // 右
}
public:
vector<string> binaryTreePaths(TreeNode* root) {
vector<string> result;
string path;
if (root == NULL) return result;
traversal(root, path, result);
return result;
}
};
迭代法:
至于非递归的方式,我们可以依然可以使用前序遍历的迭代方式来模拟遍历路径的过程,这里除了模拟递归需要一个栈,同时还需要一个栈来存放对应的遍历路径。
class Solution {
public:
vector<string> binaryTreePaths(TreeNode* root) {
stack<TreeNode*> stNode;
stack<string> stPath;
vector<string> result;
if (root) {
stNode.push(root);
stPath.push(to_string(root->val));
}
while (!stNode.empty()) {
TreeNode* node = stNode.top();
stNode.pop();
string path = stPath.top();
stPath.pop();
if (node->left == NULL && node->right == NULL) {
result.push_back(path);
}
if (node->right) {
stNode.push(node->right);
stPath.push(path + "->" + to_string(node->right->val));
}
if (node->left) {
stNode.push(node->left);
stPath.push(path + "->" + to_string(node->left->val));
}
}
return result;
}
};
404.左叶子之和
题目链接:左叶子之和
题目描述:给定二叉树的根节点
root
,返回所有左叶子之和。
解题思路:
要判断一个节点是不是左叶子节点,要根据这个节点的夫节点判断,如果root->left != NULL && root->left->left == NULL &&root->left->right == NULL
为真该节点才为左叶子节点。
递归法
使用后序遍历,计算左子树的左叶子节点之和和右子树的左叶子节点之和,相加返回给中间节点。
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
if (root == NULL)
return 0;
else if (root->left == NULL && root->right == NULL)
return 0;
int sumLeft = sumOfLeftLeaves(root->left);
if (root->left != NULL && root->left->left == NULL &&
root->left->right == NULL)
sumLeft = root->left->val;
int sumRight = sumOfLeftLeaves(root->right);
return sumLeft + sumRight;
}
};
迭代法
使用任意一种遍历方法,遍历每一个node,判断起左子节点是否满足叶子节点条件。
class Solution {
public:
int sumOfLeftLeaves(TreeNode* root) {
stack<TreeNode*> st;
if (root)
st.push(root);
int sum = 0;
while (!st.empty()) {
TreeNode* node = st.top();
st.pop();
if (node->left != NULL && node->left->left == NULL &&
node->left->right == NULL)
sum += node->left->val;
if (node->right)
st.push(node->right);
if (node->left)
st.push(node->left);
}
return sum;
}
};