二叉树递归法四道题
513.找树左下角的值
题目链接https://leetcode.cn/problems/find-bottom-left-tree-value
这道题层序遍历感觉挺好理解的,就是在循环体里面把每一层的第一个左边结点保存起来,那最后就能保存到二叉树最底层最左边结点的值了
class Solution {//层序遍历
public:
int findBottomLeftValue(TreeNode* root) {
int result = 0;
queue<TreeNode*> que;
if (root != NULL)
que.push(root);
while (!que.empty()) {
int size = que.size();
for (int i = 0; i < size; i++) {
TreeNode* node = que.front();
if (i == 0) {
result = node->val;//保存每层的第一个节点
}
if (node->left)
que.push(node->left);
if (node->right)
que.push(node->right);
que.pop();//加入这个值的左右叶子,把这个值pop掉
}
}
return result;
}
};
用递归法的话
最底层最靠左侧的值不一定是左孩子,也有可能是右孩子,假如在深度最大的那一层没有左孩子,只有一个右孩子,那么这个右孩子从左往右数是第一个值,也是最靠左的值。
思路就是要先递归到深度最大,然后再考虑左右孩子的问题。
class Solution {//正确代码
public:
int maxdepth = INT_MIN;
int result;
void traversal(TreeNode* root, int depth) {
if (root->left == NULL && root->right == NULL) {
if (depth > maxdepth) {
maxdepth = depth;
result = root->val;
return;
}
}
if (root->left) {
depth++;
traversal(root->left, depth);
depth--;//回溯
}
if (root->right) {
depth++;
traversal(root->right, depth);
depth--;//调用完后就把depth一个个返回好回到根节点重新进行调用(回溯)
}
return;
}
int findBottomLeftValue(TreeNode* root) {
traversal(root, 0);
return result;
}
};
1.确定递归函数的参数和返回值
递归应该要把根节点传进去,加上每一次要遍历的深度
2.确定终止条件
要遍历到根节点再进行保存最大深度和那个点的值。
写的时候在终止条件那有问题,结果不对,发现在进行更新的时候并不是每个节点都要更新一次,是只比较叶子结点的值,如果大于现存的最大深度,那我才保存这个点。不然就是我每遍历一个深度都要更新result的值,那在一层层返回的时候,最后result的值就会是根结点的值了。
void traversal(TreeNode* root,int depth){//错误做法
if(depth>maxdepth){
maxdepth=depth;
result=root->val;
return;
}
3.确定单层递归的逻辑
对左子树和右子树单独进行递归,而且顺序是要先遍历左边孩子的值,因为在当左右子树的深度是相等的时候,当左子树更新了result,终止条件depth>maxdepth右子树就不会进入条件,把右子树的值给覆盖了,这样result就会保存的是左子树的的值。
111.路径总和
这道题感觉跟112差不多
题目链接https://leetcode.cn/problems/find-bottom-left-tree-value
最开始在递归的时候有数据报错
原来是没有加判断条件...
if (root->left) {
targetSum -= root->left->val;
traversal(root->left, targetSum);
targetSum += root->left->val;
}
if (root->right) {
targetSum -= root->val;
traversal(root->right, targetSum);
targetSum += root->right->val;
}
这里应该是在遍历到每一个节点的时候,如果它的返回值已经为1了那就层层往上进行传递。
class Solution {//正确代码
public:
bool traversal(TreeNode* root, int targetSum) {
if (root->left == NULL && root->right == NULL && targetSum == 0) {
return 1;
}
if (root->left == NULL && root->right == NULL && targetSum != 0) {
return 0;
}
if (root->left) {
targetSum -= root->left->val;
if (traversal(root->left, targetSum))
return 1;//层层向上传递
targetSum += root->left->val;//回溯
}
if (root->right) {
targetSum -= root->val;
if (traversal(root->right, targetSum))
return 1;
targetSum += root->right->val;//回溯
}
return 0;
}
bool hasPathSum(TreeNode* root, int targetSum) {
if (root == NULL)//根节点要进行判断不然要报错
return 0;
else {
return (traversal(root, targetSum));
}
}
};
113.路径总和||
题目链接https://leetcode.cn/problems/path-sum-ii
1.确定递归函数的参数和返回值
这里求路径就不用有返回值了,直接存在数组里面,参数是每一次遍历的结点和要计算的整数目标
2.确定终止条件
遇到根节点,且参数已经被减为0的时候就可以把路径加到数组里面了,但如果不等于0的话就直接返回就行。
3.确定单层递归逻辑
左子树有值的情况下继续往左边进行遍历,把左子树的值存在path路径里面,targetSum的值进行相减来最后判断是否为0,然后进行回溯(为了在路径一次次返回后继续进行下一次路径的遍历),把path里面的值pop掉,因为在targetSum已经为0的时候终止条件res已经存储过了需要保存的路径,所以path里面的值完全没有必要进行保存,在返回的时候一个个pop掉就行,右子树同理。
class Solution {
public:
vector<int> path;
vector<vector<int>> res;
void traversal(TreeNode* root, int targetSum) {
if (root->left == NULL && root->right == NULL && targetSum == 0) {
res.push_back(path); // 终止条件
return;
}
if (root->left == NULL && root->right == NULL && targetSum != 0) {
return; // 如果已经到根节点发现值已经不和targetSum相等就可以直接return了
}
if (root->left) {
path.push_back(root->left->val);
targetSum -= root->left->val;
traversal(root->left, targetSum);
targetSum += root->left->val; // 回溯
path.pop_back(); // 回溯
}
if (root->right) {
path.push_back(root->right->val);
targetSum -= root->right->val;
traversal(root->right, targetSum); // 回溯
targetSum += root->right->val; // 回溯
path.pop_back();
}
return;
}
vector<vector<int>> pathSum(TreeNode* root, int targetSum) {
path.push_back(
root->val); // 要先把根节点加在路径里面,不然最后路径不会包含根节点
targetSum -= root->val; // 算上根节点后的targetSum就要减去根节点的值
if (root == NULL)
return res;
else {
traversal(root, targetSum);
return res;
}
}
};
106.从中序与后序遍历序列构造二叉树
题目链接https://leetcode.cn/problems/construct-binary-tree-from-inorder-and-postorder-traversal
我的妈呀,有一说一这道题看着好吓人,把思路捋清楚,莫要害怕,奥利给!
1.确定递归函数的参数和返回值
思路就是中序遍历是左中右,后序遍历是左右中,我每次递归把后序遍历的最后一个节点就是这次遍历的根节点返回,传入的参数每次都是传中序数组和后序数组。(在这后面的代码里root->left会分别指向把(左中序数组、左后序数组)传参的返回值,这样就可以把root结点最后连成二叉树返回。)
2.确定终止条件
在不停地切切切,后序不断把最后一个节点(根结点)舍弃掉,割中序又会把中间的结点(根节点)给切掉,那最后俩数组肯定得空,就直接返回NULL了,往上递归。
3.确定单层递归逻辑
1)首先通过后序数组切割中序数组
我先取到根节点(后序数组的最后一个值),然后我通过这个值找到中序数组里面它的位置,找到就break(好家伙利用完就甩),因为中序数组顺序左中右,我就可以找到左中序数组和右中序数组了(记得把中序的根节点切掉)
2)然后通过切割好的中序数组切割后序数组
通过中序数组的左边的长度我就可以切掉后序数组得到左后序数组和右后序数组。
把所有都切割好之后,就可以进行遍历了。
3)切割完后分别用root指向进行递归
中序数组切割后会得到左中序数组,右中序数组
后序数组切割后会得到左后序数组,右后序数组
用root->left(左中序数组、左后序数组) root->right(右中序数组、右后序数组)
这样root的左指针右指针就会分别遍历到应该的结点
TreeNode* traversal(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0)
return NULL;//终止条件
int rootvalue = postorder[postorder.size() - 1];
TreeNode* root = new TreeNode(rootvalue);
int divide;//找到中序数组里面根节点的位置
for (divide = 0; divide < inorder.size(); divide++) {
if (inorder[divide] == rootvalue)
break;
}
postorder.resize(postorder.size() - 1);//切掉后序的根节点
vector<int> leftinoder(inorder.begin(), inorder.begin() + divide);
vector<int> rightinoder(inorder.begin() + divide + 1, inorder.end());
//左闭右开,切掉中序的根节点
vector<int> leftpostoder(postorder.begin(), postorder.begin() + leftinoder.size());
vector<int> rightpostoder(postorder.begin() + leftinoder.size(),postorder.end());
//左闭右开,这里发现没有+1,因为后序左右中,左右的两段我都要
root->left = traversal(leftinoder, leftpostoder);
root->right = traversal(rightinoder, rightpostoder);
return root;
}
class Solution {
public:
TreeNode* buildTree(vector<int>& inorder, vector<int>& postorder) {
if (inorder.size() == 0 || postorder.size() == 0)
return NULL;
else
return traversal(inorder, postorder);
}
};