力扣669:修剪二叉搜索树,如何正确的使用递归?
递归的基本流程
在《代码随想录》里,我们了解了递归的三要素:
- 确定参数和返回值。
- 确定终止条件。
- 确定单层递归逻辑。
关于二叉树这一分类,解题思路基本都在围绕着二叉树的深度遍历和广度遍历,而二叉搜索树则几乎全部都是二叉树的深度遍历,只是将对结点的访问换成了其它的操作。
以力扣669为例,分析二叉搜索树如何使用递归。
669:修剪二叉搜索树
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树不应该改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在唯一的答案。
所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。
初次尝试
沿着三要素的方向出发:
- 参数显然不需要改变,对于每一个节点,我们都需要判断这个节点的值是否在区间内。而对于返回值,在对每一个节点进行操作使其满足区间范围之后,我们将返回上一层的函数调用,返回值要作为上一层的参数,直到最顶层。到这里,我们已经很清晰了,因为在最顶层我们需要将root返回,因此返回值是
TreeNode*。 - 终止条件,二叉搜索树的本质还是对树进行dfs,因此终止条件和dfs的一致,遇到叶子节点停止递归,按照中序遍历或者前后序遍历方式访问父节点或父节点的右子树。
- 单层递归逻辑是核心,如果结果不正确,卡在某一个例子,那么先检查自己的代码逻辑,返回值处理的是否正确?进入下一层递归的条件是否正确?需要注意的是,先处理不满足条件的情况,再处理满足条件的情况。
而在这个题中,我们要做的就是判断当前节点的值是否在区间内,不在区间内怎么处理?(以下的分析有误区,但是是写这个题的第一反应,后面会纠正)
root->val只有三种情况,第一中小于low,第二种大于high,第三种在区间内。
root->val < low时,它的左子树上所有的值都一定小于low,因此应该将当前的root更新为root->right,但是root->right也可能小于low,所以应该不断地寻找,直到右子树的值在区间内,代码如下:
if(root->val < low) {
TreeNode* cur = root;
while(cur->right && cur->right->val < low) cur = cur->right;
return cur->val < low ? cur->right : cur;
}
root->val >high时,同样的有它的右子树全部大于区间上限,因此更新当前的root,代码如下:
if(root->val > high) {
TreeNode* cur = root;
while(cur->left && cur->left->val > high) cur = cur->left;
return cur->val > low ? cur->left : cur;
}
- 而在区间范围内的节点,我们就对左子树和右子树进行剪枝,代码如下:
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
最终完整代码:
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(!root) return nullptr;
if(root->val < low) {
TreeNode* cur = root;
while(cur->right && cur->right->val < low) cur = cur->right;
return cur->val < low ? cur->right : cur;
}
if(root->val > high) {
TreeNode* cur = root;
while(cur->left && cur->left->val > high) cur = cur->left;
return cur->val > low ? cur->left : cur;
}
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
};
修改三层递归逻辑
上面的代码在测试用例:

报错,其实再回看代码,有一个很显然的错误就是,如果根节点不在区间内,那么就直接返回其左右子树,而不会对左右子树进行剪枝,因此对于root->val < low和root->val > high的处理,不应该是直接返回在区间内的左子树节点或者右子树节点,应该修改为,继续进入左子树或者右子树进行剪枝。
1.和2.的代码逻辑更改为:
if(root->val < low) {
return trimBST(root->right, low, high);
}
if(root->val > high) {
return trimBST(root->left, low, high);
}
更改后的全部代码是:
class Solution {
public:
TreeNode* trimBST(TreeNode* root, int low, int high) {
if(!root) return nullptr;
if(root->val < low) {
return trimBST(root->right, low, high);
}
if(root->val > high) {
return trimBST(root->left, low, high);
}else{
root->left = trimBST(root->left, low, high);
root->right = trimBST(root->right, low, high);
return root;
}
}
};
二叉搜索树几乎都可以用递归解决,想要练习递归建议把这部分的题归类总结一下。一个个人的疑问就是,递归需要频繁的调用函数,在用栈可以解决的情况下,是否优先用栈,而不是递归。

浙公网安备 33010602011771号