算法day20|669. 修剪二叉搜索树、将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树
算法day20|669. 修剪二叉搜索树、将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树
- 669. 修剪二叉搜索树
- 08.将有序数组转换为二叉搜索树
- 538.把二叉搜索树转换为累加树
669. 修剪二叉搜索树
class Solution {
public:
TreeNode* traversal(TreeNode* root, int low, int high)
{
if(root==nullptr)
return nullptr;
if(root->val<low)
{
TreeNode*right=traversal(root->right,low,high);
return right;
}
if(root->val>high)
{
TreeNode*left=traversal(root->left,low,high);
return left;
}
root->left=traversal(root->left,low,high);
root->right=traversal(root->right,low,high);
return root;
}
TreeNode* trimBST(TreeNode* root, int low, int high) {
return traversal(root,low,high);
}
};
重点讲单层递归逻辑:首先,题目本质要求、返回值和逻辑三者是紧密结合的。题目要求返回最后树的根节点,那么我们的返回值肯定要选节点类型。再选择了返回值之后,我们移除节点的逻辑是什么样的呢?答:如果这个结点真的需要删除,那我们就用它可能成立的子树去替代,也就是说,这个结点被它的子树覆盖了。如果不是,该节点还是该节点。
本质就是检查是否“德不配位”:如果配位,你还在这个位置上;如果不配位,你就要下台,让你下面的子孙来顶替。当然,这子孙也要经过考验,所以实际是被考验后的子孙替代。
由于对当前结点的处理不需要借助孩子的信息,所以使用先序。当父亲确定好之后才能正常去遍历。
二叉树的修剪与删除的区别点在哪里?答:
- 首先删除的核心操作在终止条件下进行,当遇到要删除的结点时,整个遍历过程其实已经结束了;而移除是对当前结点的处理。核心不同点:终止条件里面是不能使用递归的,因为到这里递归一斤停止了;而对当前结点的处理是可以使用递归的。这也是判断是否区分两者的重要依据,所以使用和判断相辅相成。
- 其次,
两者共同点:
- 都是用root->left或者root->right来接收递归返回值,因为在每一次递归,结点都要经过审判,如果满足某种条件它就要被剔除或者替代,所以这是直接面向结点的、决定结点命运和生死的操作,所以直接用结点本身来接收。
- 每一次返回的值都是对当前结点的替代节点。可以是Null、可以是其他结点,也可以是它本身。这个和用root->left或者root->right来接收递归返回值是息息相关的,所以两者是统一考虑的。
08.将有序数组转换为二叉搜索树
class Solution {
public:
TreeNode* traversal(vector<int>& nums,int left,int right)
{
if(left>right)
return nullptr;
int mid=(left+right)/2;
TreeNode* root=new TreeNode(nums[mid]);
root->left=traversal(nums,left,mid-1);
root->right=traversal(nums,mid+1,right);
return root;
}
TreeNode* sortedArrayToBST(vector<int>& nums) {
return traversal(nums,0,nums.size()-1);
}
};
建立树肯定是从根节点往下建立的,所以关键是找到该插入数组中的哪一项。
我们在原数组中用两个指针,表示区间的边界(左闭右闭),通过计算,mid指针恰好指向要插入的元素。采用先序遍历,在当前结点处理完之后,左孩子用左区间,右孩子用右区间。
易错:
- 终止条件,这里的终止条件不是数组的size为0,因为我们并没有对数组做任何的改变,我们只是用了两个指针来限定区间,所以终止条件肯定与两指针的位置有关。
- 遵循循环不变量的原则,自始至终都遵循同一个区间标准,这里就是左闭右闭。
538.把二叉搜索树转换为累加树
class Solution {
public:
int pre=0;
void traversal(TreeNode*root)
{
if(root==nullptr)
return;
traversal(root->right);
root->val+=pre;
pre=root->val;
traversal(root->left);
return;
}
TreeNode* convertBST(TreeNode* root) {
if(root==nullptr)
return nullptr;
traversal(root);
return root;
}
};
做这题需要巧劲,我一开始是从前往后加的,时间复杂度高不说,中间过程还非常繁琐,导致出错。而正确的思路是从后往前加,用pre指针记录之前的值,从而实现了边遍历边加。所以有时候按常规思维做题很费劲的时候,不妨停下来,换一种思路再想一遍。