找树左下角的值
本地递归偏难,反而迭代简单属于模板题, 两种方法掌握一下
题目链接/文章讲解/视频讲解:代码随想录
递归方法。可以使用前序遍历(当然中序,后序都可以,因为本题没有中间节点的处理逻辑,只要左优先就行),保证每次到达新的一层,都会优先遍历到最左边的元素——
class Solution {
public:
int maxdepth;
int result;
void traversal(TreeNode* node,int depth){
if(node->left) traversal(node->left,depth+1);
if(!node->left&&!node->right&&depth>maxdepth) {
result=node->val;
maxdepth=depth;
}
if(node->right) traversal(node->right,depth+1);
}
int findBottomLeftValue(TreeNode* root) {
traversal(root,1);
return result;
}
};
迭代法,使用层序遍历即可。
路径总和
本题 又一次设计要回溯的过程,而且回溯的过程隐藏的还挺深,建议先看视频来理解 。112. 路径总和,和 113. 路径总和ii 一起做了。 优先掌握递归法。
题目链接/文章讲解/视频讲解:代码随想录
这道题很有回溯法的风范了,是一道很典型的回溯题,每次到达一个节点是否是满足要求的路径的一部分并不确定,需要不断尝试。
112. 路径总和
递归方法。依旧还是通过值传递来实现回溯——
bool hasPathSum(TreeNode* root, int targetSum) {
if(root==NULL) return false;
if(targetSum-root->val==0&&!root->left&&!root->right) return true;
bool left=false,right=false;
if(root->left) left=hasPathSum(root->left,targetSum-root->val);
if(root->right) right=hasPathSum(root->right,targetSum-root->val);
return left||right;
}
迭代法。由于要实现的递归函数有两个参数,故而所用的栈的里面的数据类型是pair<TreeNode*,int>——
bool haspathsum(TreeNode* root, int sum) {
if (root == null) return false;
// 此时栈里要放的是pair<节点指针,路径数值>
stack<pair<TreeNode*, int>> st;
st.push(pair<TreeNode*, int>(root, root->val));
while (!st.empty()) {
pair<TreeNode*, int> node = st.top();
st.pop();
// 如果该节点是叶子节点了,同时该节点的路径数值等于sum,那么就返回true
if (!node.first->left && !node.first->right && sum == node.second) return true;
// 右节点,压进去一个节点的时候,将该节点的路径数值也记录下来
if (node.first->right) {
st.push(pair<TreeNode*, int>(node.first->right, node.second + node.first->right->val));
}
// 左节点,压进去一个节点的时候,将该节点的路径数值也记录下来
if (node.first->left) {
st.push(pair<TreeNode*, int>(node.first->left, node.second + node.first->left->val));
}
}
return false;
}
113. 路径总和ii
随时记录路径节点,如果满足条件就放进结果数组中即可。
从中序与后序遍历序列构造二叉树
本题算是比较难的二叉树题目了,大家先看视频来理解。 106.从中序与后序遍历序列构造二叉树,105.从前序与中序遍历序列构造二叉树 一起做,思路一样的
题目链接/文章讲解/视频讲解:代码随想录
主要的依据是后序遍历(左 右 根)和中序遍历(左 根 右)之间的联系,首先根据后序遍历能很容易确定每次根节点的数值(也就是数组的最后一个值),判断是否为叶子节点(遍历数组只有一个数值),如果叶子节点那就直接返回构造的根节点;接着根据这个值试图将中序遍历的数组分成左边部分和右边部分,然后再将后序遍历分成左边部分和右边部分(根据中序的左边部分的节点下标和后序左边部分的节点下标一一对应),来进一步实现递归。
这道题的一个难点是对于区间的划分,注意都是同一种区间划分即可,文章统一左闭右开区间。
关于这道题的拓展,如果有前序遍历和中序遍历也同样能够确定一棵二叉树,但是如果是前序后序遍历两个就不行了,因为没办法确定左边部分和右边部分的界限。
class Solution {
private:
// 中序区间:[inorderBegin, inorderEnd),后序区间[postorderBegin, postorderEnd)
TreeNode* traversal (vector<int>& inorder, int inorderBegin, int inorderEnd, vector<int>& postorder, int postorderBegin, int postorderEnd) {
if (postorderBegin == postorderEnd) return NULL;
int rootValue = postorder[postorderEnd - 1];
TreeNode* root = new TreeNode(rootValue);
if (postorderEnd - postorderBegin == 1) return root;
int delimiterIndex;
for (delimiterIndex = inorderBegin; delimiterIndex < inorderEnd; delimiterIndex++) {
if (inorder[delimiterIndex] == rootValue) break;
}
// 切割中序数组
// 左中序区间,左闭右开[leftInorderBegin, leftInorderEnd)
int leftInorderBegin = inorderBegin;
int leftInorderEnd = delimiterIndex;
// 右中序区间,左闭右开[rightInorderBegin, rightInorderEnd)
int rightInorderBegin = delimiterIndex + 1;
int rightInorderEnd = inorderEnd;
// 切割后序数组
// 左后序区间,左闭右开[leftPostorderBegin, leftPostorderEnd)
int leftPostorderBegin = postorderBegin;
int leftPostorderEnd = postorderBegin + delimiterIndex - inorderBegin; // 终止位置是 需要加上 中序区间的大小size
// 右后序区间,左闭右开[rightPostorderBegin, rightPostorderEnd)
int rightPostorderBegin = postorderBegin + (delimiterIndex - inorderBegin);
int rightPostorderEnd = postorderEnd - 1; // 排除最后一个元素,已经作为节点了
root->left = traversal(inorder, leftInorderBegin, leftInorderEnd, postorder, leftPostorderBegin, leftPostorderEnd);
root->right = traversal(inorder, rightInorderBegin, rightInorderEnd, postorder, rightPostorderBegin, rightPostorderEnd);
return root;
}
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0) return NULL;
// 左闭右开的原则
return traversal(inorder, 0, inorder.size(), postorder, 0, postorder.size());
}
};