算法学习打卡day22| 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

669. 修剪二叉搜索树

力扣题目链接
题目描述:
给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 (即,如果没有被移除,原有的父代子代关系都应当保留)。 可以证明,存在 唯一的答案 。

所以结果应当返回修剪好的二叉搜索树的新的根节点。注意,根节点可能会根据给定的边界发生改变。

示例 1:
在这里插入图片描述

输入:root = [1,0,2], low = 1, high = 2
输出:[1,null,2]

思路:

  • 递归法,这道题递归法比较简单,采用中序遍历,如果跟节点值比low小,证明区间在树的右子树上,就往右走,如果跟节点值比high大,说明区间在左子树上,就往左走,如果根节点恰好在区间里,那么就把根节点不动,去分别递归左右子树,返回的结果接在根节点的左右指针上就行。
  • 递归三部曲:
    • 确定函数参数和返回值:函数参数就是根节点和lowhigh,返回值为根节点
    • 函数退出条件:根节点为空的时候
    • 单步处理逻辑:当root->val > high时,递归左子树,当root->val < low时,递归右子树,当在区间内时,就分别递归左右子树然后返回值接到root的左右就好。

代码实现:

TreeNode* traversal(TreeNode* root, int low, int high) {
        if (root == nullptr)    return nullptr;
        if (root->val > high)   return traversal(root->left, low, high);
        if (root->val < low)    return traversal(root->right, low, high);
        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,如果比最大值还要大,那就到左子树上去找root,直到root[low,high]里了(注意是闭区间)
    • 找到root后,对左右子树进行剪枝,因为左子树上可能有比low小的,右子树上可能有比high大的。
    • 怎么剪枝?
      • 剪左子树的时候,我们要剪掉比low小的,那么就去判断cur的左子树是不是比low小,如果比low小,那么它左子树的左子树就都剪掉了,把左子树的右子树接到cur到左子树上,此时cur的左子树已经更新,然后继续对cur的左子树判断,直到全部遍历完。
      • 剪右子树和左子树一样的逻辑,去找cur的右子树,只要比high大,就把右子树的左子树接到cur的右子树上,依此类推。

代码实现:

TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (root == nullptr)    return nullptr;
        //找合适的根节点
        while (root && (root->val < low || root->val > high)) {
            if (root->val < low) {
                root = root->right;
            } else {
                root = root->left;
            }
        }
        TreeNode* cur = root;   //把根节点存起来,做完剪枝返回root,这里用cur去遍历
        //剪枝左子树
        while (cur) {
            while(cur->left && cur->left->val < low) {
                cur->left = cur->left->right;
            }
            cur = cur->left;
        }
        //剪枝右子树
        cur = root;//回溯到root用于遍历右子树
        while (cur) {
            while(cur->right && cur->right->val > high) {
                cur->right = cur->right->left;
            }
            cur = cur->right;
        }
        return root;
    }

108.将有序数组转换为二叉搜索树

力扣题目链接
题目描述:
给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。

高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。

示例 1:

在这里插入图片描述

输入:nums = [-10,-3,0,5,9]
输出:[0,-3,9,-10,null,5]
解释:[0,-10,5,null,-3,null,9]

思路:

  • 构造一棵树,先找根节点,然后再找左右节点,接到根节点上就行,利用平衡二叉搜索树的性质,正好数组有序,我们每次取数组的中间元素作为根节点,然后递归左右子区间就好了。
  • 递归三部曲:
    • 确定函数参数和返回值:函数参数为数组nums和区间的起始位置和结束位置(注意左闭右开还是左闭右闭,循环不变量!!!,很重要)
    • 函数退出条件:当用左闭右开时begin大于等于end时退出,左闭右闭时,begin大于end时退出。
    • 单步处理逻辑,取begin和end的中间值作为根节点,然后取递归左右子区间,把返回值接到root上即可。

代码实现

TreeNode* traversal(vector<int>& nums, int begin, int end) {
        if (begin > end)   return nullptr;
        int root_index = begin + (end - begin) / 2;
        TreeNode* root = new TreeNode(nums[root_index]);
        root->left = traversal(nums, begin, root_index - 1);
        root->right = traversal(nums, root_index + 1, end);
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return traversal(nums, 0, nums.size() - 1);
    }

538.把二叉搜索树转换为累加树

力扣题目链接
题目描述:
给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

提醒一下,二叉搜索树满足下列约束条件:

节点的左子树仅包含键 小于 节点键的节点。
节点的右子树仅包含键 大于 节点键的节点。
左右子树也必须是二叉搜索树。
注意:本题和 1038: https://leetcode-cn.com/problems/binary-search-tree-to-greater-sum-tree/ 相同

示例 1:

在这里插入图片描述

输入:[4,1,6,0,2,5,7,null,null,null,3,null,null,null,8]
输出:[30,36,21,36,35,26,15,null,null,null,33,null,null,null,8]

思路:

  • 递归法:这道题不用改变树结构,直接在遍历树的时候操作即可,和二叉搜索树的众数二叉搜索树最小绝对值差 两道题差不多。
  • 这里还是采用中序遍历,但是需要变一下,因为本题需要把每个节点值改为大于它的所有节点的值之和,而普通的中序遍历得到的是升序,我们得到的是比当前节点小的节点值之和,其实我们交换一下中序遍历左右子树的递归顺序,就得到的降序了,这样访问过的节点都是比自己大的节点了,剩下就很简单了,看代码!。

代码实现

int pre = 0;
    void traversal(TreeNode* root) {
        if (root == nullptr)    return;
        traversal(root->right);
        root->val += pre;//pre初始为0,
        /*后序都是前一个节点的值了,因为前一个节点已经更新了,
        所以不用记录前面节点之和了)*/
        pre = root->val;
        traversal(root->left);
    }
    TreeNode* convertBST(TreeNode* root) {
        if (root == nullptr)    return nullptr;
        traversal(root);
        return root;
    }
  • 迭代法:套用中序遍历的模版就行,但是需要交换一下左右遍历顺序

代码实现:

TreeNode* convertBST(TreeNode* root) {
        if (root == nullptr)    return nullptr;

        stack<TreeNode*> stk;
        TreeNode* cur = root;
        int pre = 0;
        while (cur || !stk.empty()) {
            if (cur) {
                stk.push(cur);
                cur = cur->right;
            } else  {
                cur = stk.top();
                cur->val += pre;
                pre = cur->val;
                stk.pop();
                cur = cur->left;
            }  
        }
        return root;
    }
  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值