代码随想录DAY21 - 二叉树 - 08/20

目录

修建二叉搜索树

题干

思路和代码

递归法

迭代法

将有序数组转化为平衡二叉搜索树

题干

思路和代码

递归法

递归优化

迭代法

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

题干

思路和代码

递归法

迭代法


修建二叉搜索树

题干

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

说明:

  • 每个结点值非负数,0 <= Node.val <= 104

  • 树中每个节点的值都是 唯一 的

链接:. - 力扣(LeetCode)

思路和代码

先判断根节点是否在 [low, high] 的范围内,如果不是则删除,并从左右子树中找到新的根节点;如果是则递归修剪左右子树。

递归法
  • 递归参数和返回值:参数是传入的根节点,闭区间的最小边界 low、最大边界 high;返回值是修建过的新子树的根节点。

  • 递归结束的条件:当传入的节点为空,代表没有子树需要修建,返回空。

  • 递归顺序:前序遍历,先判断根节点是否在 [low, high] 的范围内,如果不是则删除,并从左右子树中找到新的根节点;如果是则递归修剪左右子树。

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (root == nullptr) return nullptr;
        if (root->val < low){
            // 根结点包括根节点的左子树都要被删除,新的根节点应为修剪过的右子树
            root = trimBST(root->right,low,high);
        } else if (root->val > high){
            // 根结点包括根节点的右子树都要被删除,新的根节点应为修剪过的左子树
            root = trimBST(root->left,low,high);
        } else{
            // 根节点在 [low, high] 范围内,修剪左右子树
            if (root->left) root->left = trimBST(root->left,low,high);
            if (root->right) root->right = trimBST(root->right,low,high);
        }
        return root;
    }
};
迭代法

迭代法暂时不太理解思路,需要二刷的时候复盘。

将有序数组转化为平衡二叉搜索树

题干

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

链接:. - 力扣(LeetCode)

思路和代码

要让二叉搜索树平衡,也就是让左右子树的高度差尽量相同,即让左右子树的节点数目尽量相同。那么只需每次建树时让序列中点作为根节点,再让左右子树建树。

递归法
  • 递归参数和返回值:参数即传入的序列数组,返回值是根据序列构建好的二叉树的根节点,需要将返回值传递给上层父节点。

  • 递归结束的条件:当传入的序列数组为空,返回空节点。

  • 递归顺序:前序遍历,每次让序列中点作为根节点,再递归构建左右子树。

class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        int size = nums.size();
        if (size == 0) return nullptr;
        TreeNode* root = new TreeNode(nums[size/2]); // 让序列中点为根节点
        vector<int> leftTree(nums.begin(),nums.begin()+size/2); // 左子树序列
        root->left = sortedArrayToBST(leftTree); // 递归建立左子树
        vector<int> rightTree(nums.begin()+size/2+1,nums.end()); // 右子树序列
        root->right = sortedArrayToBST(rightTree); // 递归建立右子树
        return root;
    }
};
递归优化

在上一个方法中我们新建了左子树和右子树的序列数组,但其实只需要知道左子树和右子树的序列下标即可。因此在这里我们的递归参数需要传递序列下标。

  • 递归参数和返回值:参数是原始的序列数组、要构建子树的序列下标 low、high;返回值是根据序列构建好的二叉树的根节点。

  • 递归结束的条件:当传入的序列数组为空,返回空节点。

  • 递归顺序:前序遍历,每次让序列中点作为根节点,再递归构建左右子树。

class Solution {
public:
    // 左闭右闭
    TreeNode* buildTree(vector<int>& nums, int low, int high){
        if (high < low) return nullptr; // 序列为空,返回空节点
        int mid = (low+high)/2; // 记录中点位置
        TreeNode* root = new TreeNode(nums[mid]); // 让中点为根节点
        root->left = buildTree(nums,low,mid-1); // 建立左子树
        root->right = buildTree(nums,mid+1,high); // 建立右子树
        return root;
    }
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        return buildTree(nums,0,nums.size()-1);
    }
};
迭代法

在递归法中我们每次都先根据序列中点建立根节点,再以中点划分左右子树,获取左右子树的区间下标。在迭代法中,我们采取相同思路,用三个队列分别存储新建的根节点、左子树区间、右子树区间。每次都从根节点序列中取出根节点,令根节点的左孩子 = 左子树区间中点,根节点的右孩子 = 右子树区间的中点,再将根节点的左右孩子插入节点队列中,如此循环。

class Solution {
public:
    TreeNode* sortedArrayToBST(vector<int>& nums) {
        int size = nums.size();
        TreeNode* root = new TreeNode(nums[size/2]); // 建立根节点
        
        queue<TreeNode*> nodes; // 记录建立的节点
        nodes.push(root); // 初始为根节点
        
        queue<vector<int>> leftTree; // 记录左子树的下标范围
        leftTree.push({0,size/2-1});
        
        queue<vector<int>> rightTree; // 记录右子树的下标范围
        rightTree.push({size/2+1,size-1});
        
        while (!nodes.empty()){
            TreeNode* father = nodes.front(); nodes.pop(); // 记录父节点
            // 取出左子树区间
            int leftLow = leftTree.front()[0];
            int leftHigh = leftTree.front()[1];
            int leftMid = (leftLow+leftHigh)/2;
            leftTree.pop();
            // 当区间有效时,建立左子树
            if (leftLow <= leftHigh) father->left = new TreeNode(nums[leftMid]);
            // 取出右子树区间
            int rightLow = rightTree.front()[0];
            int rightHigh = rightTree.front()[1];
            int rightMid = (rightLow+rightHigh)/2;
            rightTree.pop();
            // 当区间有效时,建立右子树
            if (rightLow <= rightHigh) father->right = new TreeNode(nums[rightMid]);
            // 继续划分左右子树
            if (leftLow < leftHigh){
                nodes.push(father->left); // 加入左子树根节点
                // 以左子树为根节点划分左右子树
                leftTree.push({leftLow,leftMid-1}); 
                rightTree.push({leftMid+1,leftHigh});
            }
            if (rightLow < rightHigh){
                nodes.push(father->right); // 加入右子树根节点
                // 以右子树为根节点划分左右子树
                leftTree.push({rightLow,rightMid-1});
                rightTree.push({rightMid+1,rightHigh});
            }
        }
        return root;
    }
};

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

题干

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

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

  • 节点的左子树仅包含键 小于 节点键的节点。

  • 节点的右子树仅包含键 大于 节点键的节点。

  • 左右子树也必须是二叉搜索树。

链接:. - 力扣(LeetCode)

思路和代码

由于是二叉搜索树,则 大于等于原节点的值 肯定都在该节点自身及其右子树中。累加的时候可以根据 “右中左” 的顺序遍历二叉树,二叉树的最右节点肯定是二叉树的最大值,没有比他更大的,该节点作为起点,之后再遍历中间节点、左子树。这样每个节点的新值 = 上一个节点的值 + 自己原来的值。

递归法
  • 递归参数和返回值:参数即传入的根节点,无返回值,在递归过程中一步步修改节点值。

  • 递归结束的条件:当传入的节点为空,返回空。

  • 递归顺序:根据 ”右中左“ 的顺序遍历二叉树,先递归右子树,修改中间节点,再递归左子树。

class Solution {
public:
    TreeNode* pre = nullptr; // 记录 遍历过的上一个节点
    void sum(TreeNode* root){ // 计算累加和
        if (root == nullptr) return;
        sum(root->right); // 先遍历右子树
        if (pre != nullptr){
            root->val += pre->val; // 累加节点值
        }
        pre = root;
        sum(root->left); // 遍历左子树
    }
    TreeNode* convertBST(TreeNode* root) {
        sum(root);
        return root;
    }
};
迭代法

思路和之前的方法相同,只不过把反中序递归改为反中序迭代遍历,属于模板题。

class Solution {
public:
    TreeNode* convertBST(TreeNode* root) {
        TreeNode* cur = root;
        stack<TreeNode*> tmpNode;
        TreeNode* pre = nullptr; // 记录 cur 的上一个节点
        while (cur != nullptr || !tmpNode.empty()){
            if (cur != nullptr){
                tmpNode.push(cur);
                cur = cur->right; // 一直遍历右子树直到找到最右节点
            } else{
                // 找到了最右节点
                cur = tmpNode.top();
                tmpNode.pop();
                if (pre != nullptr){
                    cur->val += pre->val; // 修改节点值
                }
                pre = cur;
                cur = cur->left; // 遍历左子树
            }
        }
        return root;
    }
};
  • 26
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值