第二十三天| 669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树

Leetcode 669. 修剪二叉搜索树

题目链接:669 修剪二叉搜索树

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

思考一:递归法。终止条件:若当前节点为空则返回空。单层递归逻辑:如果当前节点val值大于最大区间值,递归处理其左子树并返回。如果当前节点val值小于最小区间值,递归处理其右子树并返回。如果当前节点val值在区间内,则递归处理左右子树。

代码:

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (!root)  return nullptr;
        if (root->val > high)       //当前节点val值大于区间最大值时递归处理其左子树
            return trimBST(root->left, low, high);

        if (root->val < low)        //当前节点val值小于区间最小值时递归处理其右子树
            return trimBST(root->right, low, high);

        root->left = trimBST(root->left, low ,high);
        root->right = trimBST(root->right, low ,high);
        return root;
    }
};

思考二:迭代法。由于二叉搜索树的节点有序性,遍历过程无需借助栈和队列。先将root移动到区间范围内,再处理其左右子树。

代码:

class Solution {
public:
    TreeNode* trimBST(TreeNode* root, int low, int high) {
        if (!root)  return nullptr;

        //处理头节点 让root移动到区间内
        while (root && (root->val > high || root->val < low)) {
            if (root->val > high)   root = root->left;
            else    root = root->right;
        }
        TreeNode* cur = root;       //记录根节点
        //处理左子树
        while (cur) {
            while (cur->left && cur->left->val < low)
                cur->left = cur->left->right;
            cur = cur->left;
        }
        cur = root;     //当前节点改为根节点
        //处理右子树
        while (cur) {
            while (cur->right && cur->right->val > high)
                cur->right = cur->right->left;
            cur = cur->right;
        }

        return root;
    }
};

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

题目链接:108 将有序数组转换为二叉搜索树

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

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

思考一:递归法。由于要求高度平衡,则每次递归从数组区间中间位置取值作为节点元素,再以此位置分割数组区间,分别处理左右区间作为当前节点的左右子树。

代码:

class Solution {
public:
    TreeNode* traversal(vector& nums, int left, int right) {
        if (left >= right)  return nullptr;
        int middle = (left + right) / 2;
        TreeNode* root = new TreeNode(nums[middle]);
        //分割递归处理  左闭右开
        root->left = traversal(nums, left, middle);
        root->right = traversal(nums, middle + 1, right);
        return root;
    }

    TreeNode* sortedArrayToBST(vector& nums) {
        if (nums.size() == 0)  return nullptr;
        return traversal(nums, 0, nums.size());
    }
};

思考二:迭代法。通过三个队列来模拟递归传值过程,一个队列放遍历的节点,一个队列放左区间下标,一个队列放右区间下标。

代码:

class Solution {
public:
    TreeNode* sortedArrayToBST(vector& nums) {
        if (nums.size() == 0) return nullptr;

        TreeNode* root = new TreeNode(0);   // 初始根节点
        queue nodeQue;           // 放遍历的节点
        queue leftQue;                 // 保存左区间下标
        queue rightQue;                // 保存右区间下标
        nodeQue.push(root);                 // 根节点入队列
        leftQue.push(0);                    // 0为左区间下标初始位置
        rightQue.push(nums.size() - 1);     // nums.size() - 1为右区间下标初始位置

        while (!nodeQue.empty()) {
            TreeNode* curNode = nodeQue.front();
            nodeQue.pop();
            int left = leftQue.front(); leftQue.pop();
            int right = rightQue.front(); rightQue.pop();
            int mid = left + ((right - left) / 2);

            curNode->val = nums[mid];       // 将mid对应的元素给中间节点

            if (left <= mid - 1) {          // 处理左区间
                curNode->left = new TreeNode(0);
                nodeQue.push(curNode->left);
                leftQue.push(left);
                rightQue.push(mid - 1);
            }

            if (right >= mid + 1) {         // 处理右区间
                curNode->right = new TreeNode(0);
                nodeQue.push(curNode->right);
                leftQue.push(mid + 1);
                rightQue.push(right);
            }
        }
        return root;
    }
};

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

题目链接:538 把二叉搜索树转换为累加树

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

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

  • 节点的左子树仅包含键 小于 节点键的节点。
  • 节点的右子树仅包含键 大于 节点键的节点。
  • 左右子树也必须是二叉搜索树。

思考一:递归法。由于二叉搜索树的节点有序性,任意节点的右子树键值均大于节点键值。故采取右中左的顺序遍历二叉树,遍历过程顺便记录前一个节点键值。

代码:

class Solution {
public:
    int pre = 0;        //记录前一个结点键值
    TreeNode* convertBST(TreeNode* root) {
        if (!root)  return nullptr;
        root->right = convertBST(root->right);      //右
        root->val += pre;        //中
        pre = root->val;        //更新键值
        root->left = convertBST(root->left);        //左
        return root;
    }
};

思考二:迭代法。修改中序遍历的顺序迭代法的节点遍历顺序为右中左,同时修改中节点处理逻辑为修改键值和更新记录(记录前一个结点键值)。

中序遍历迭代法写法:第十四天| 二叉树的递归遍历、二叉树的迭代遍历、二叉树的统一迭代法

代码:

class Solution {
public:
    TreeNode* convertBST(TreeNode* root) {
        stack st;
        TreeNode* cur = root;
        int pre = 0;
        while (cur || !st.empty()) {
            if (cur) {
                st.push(cur);
                cur = cur ->right;      //右
            } else {
                cur = st.top(); st.pop();
                cur->val += pre;        //中
                pre = cur->val;
                cur = cur->left;        //左
            }
        }
        return root;
    }
};

 二叉树专题总结:

  • 熟悉二叉树的前中后序遍历以及层序遍历。
  • 递归三部曲的练习:
    • 确定递归函数的参数和返回类型
      • 如果需要搜索整棵二叉树且不用处理递归返回值,递归函数就不要返回值。
      • 如果需要搜索整棵二叉树且需要处理递归返回值,递归函数就需要返回值。
      • 如果要搜索其中一条符合条件的路径,那么递归一定需要返回值,因为遇到符合条件的路径了就要及时返回。
    • 确定终止条件
    • 确定单层递归的逻辑
      • 考虑每题不同二叉树的各类情况
      • 明确是否需要遍历整棵二叉树以及左、中、右节点的遍历顺序
  • 迭代方式,采用栈和队列来辅助。其中的统一迭代法采用标记法来确定节点是否访问过。
  • 构造二叉树要坚持区间不变量原则。注意类似用数组构造二叉树的题目,每次分隔尽量不要定义新的数组,而是通过下标索引直接在原数组上操作,这样可以节约时间和空间上的开销。
  • 二叉树题目总结:
    • 涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。

    • 求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算。

    • 求二叉搜索树的属性,一定是中序,利用好节点的有序性。

你可能感兴趣的:(代码随想录算法训练营,算法,leetcode)