代码随想录算法训练营第十八天 | 二叉搜索树

Day 18 总结

  • 自己实现中遇到哪些困难
    • 501.二叉搜索树中的众数,更新记录的时候需要每一次读取数字的时候就更新,而不是等待数字切换的时候再更新。
  • 今日收获,记录一下自己的学习时间
    • 13:15 - 14:10
    • 15:00 - 
    • 二叉搜索树,想到收集成数组,进行全局处理
    • List转数组
    • List list;
      return list.stream().mapToInt(Integer::intValue).toArray();

二叉树小总结

  • 二叉树合并
  • 二叉搜索树的特性,中序遍历是有序数组
  • 二叉搜索树的陷阱,需要从全局确认,小心极值的比较
  • 公共祖先问题,从下到上
  • 二叉树,平衡树,完全树,搜索树,满树
  • 二叉搜索树插入,删除

530.二叉搜索树的最小绝对差(简单)

题目链接/文章讲解:代码随想录

题目链接:https://leetcode.cn/problems/minimum-absolute-difference-in-bst/

实现思路

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。

把二叉搜索树树转成中序数组,比较两个数字之间的差即可。在数组上就可以以全局角度进行查看。

代码实现

class Solution {
    public List list = new ArrayList<>();

    public int getMinimumDifference(TreeNode root) {
        traverse(root);
        int[] arr = list.stream().mapToInt(Integer::intValue).toArray();
        int res = Integer.MAX_VALUE;
        for (int i=1; i

501.二叉搜索树中的众数(简单)

题目链接/文章讲解:代码随想录

题目链接:https://leetcode.cn/problems/find-mode-in-binary-search-tree/

实现思路

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。

如果树中有不止一个众数,可以按 任意顺序 返回。

二叉搜索树转中序数组,使用变量记录当前数字以及遇到的次数,使用一个List记录所有的结果,如果记录更新了,就刷新List。

代码实现

class Solution {
    public int[] findMode(TreeNode root) {
        List list = new ArrayList<>();
        traverse(root, list);

        int count = 0;
        int maxCount = 0;
        int pre = Integer.MAX_VALUE;
        List result = new ArrayList<>();
        for (int i=0; i maxCount) {
                result.clear();
                maxCount = count;
                result.add(num);
            }
        }
        return result.stream().mapToInt(Integer::intValue).toArray();
    }

    public void traverse(TreeNode node,List list) {
        if (node == null) return;

        traverse(node.left, list);
        list.add(node.val);
        traverse(node.right, list);
    }
}

236. 二叉树的最近公共祖先(简单)

题目链接/文章讲解:代码随想录

题目链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-tree/description/

实现思路

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先

如果某个节点是两个节点p,q的公共祖先,那么q,p一定在node节点的左右子树当中。搜索该树的左右节点,观察两个子树当中是否出现过这两个节点。

代码实现

class Solution {
    public TreeNode result;

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return traverse(root, p, q);
    }

    // 1. 递归参数和返回值
    // 参数: p节点,q节点,当前节点
    // 返回值:返回最近的公共节点
    public TreeNode traverse(TreeNode node, TreeNode p, TreeNode q) {
        // 2. 终止条件
        // 遇到空节点,或者找到了p,q
        if (node == null || node == p || node == q) {return node;}

        // 3. 单层递归逻辑
        // 后序遍历
        TreeNode left = traverse(node.left, p, q);
        TreeNode right =  traverse(node.right, p, q);
        // 检查左右树的返回结果
        // 如果左树右树都有找到了,说明当前节点就是公共祖先
        if (left != null && right != null) {return node;}
        // 左数找到了结果,右树没有,说明公共节点在左树里
        if (left != null) {return left;}
        // 左数没找到,右数找到了,说明公共节点在右数
        return right;

        // 第二种递归逻辑
        // 左树没有,就只能看右树的结果了
        if (left == null) return right;
        // 左树有,但是右树没有,就只能看左树的结果了
        if (right == null) return left;
        // 左树右树都有,说明当前节点是公共节点
        return node;
    }

}

235. 二叉搜索树的最近公共祖先

题目链接/文章讲解:代码随想录

题目链接:https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/

实现思路

二叉搜索树和普通二叉树区别在于有序,通过比较顺序可以确定左右分支要搜索哪一个。

代码实现

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        return traverse(root, p, q);
    }

    public TreeNode traverse(TreeNode node, TreeNode p, TreeNode q) {
        if (node == null) {return node;}

        if (node.val > p.val && node.val > q.val) { // 说明公共节点在左树
            return traverse(node.left, p, q);
        }
        if (node.val < p.val && node.val < q.val) { // 说明公共节点在右树
            return traverse(node.right, p, q);
        }
        // 不在左树,也不在右树,就是当前节点
        return node;
    }
}

701.二叉搜索树中的插入操作

题目链接/文章讲解:代码随想录

题目链接:https://leetcode.cn/problems/insert-into-a-binary-search-tree/

实现思路

给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

插入到底层null节点,就搜索到一个合适的位置插入就行。不需要改变原来的树的结构。

代码实现

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        return traverse(root, val);
    }

    // 参数:当前节点,需要插入的值
    // 返回值: 返回插入之后的当前节点
    public TreeNode traverse(TreeNode node, int val) {
        // 终止条件
        if (node == null) {return new TreeNode(val);}

        // 向左树插入
        if (node.val > val) {node.left = traverse(node.left, val);}
        // 向右树插入
        if (node.val < val) {node.right = traverse(node.right, val);}
        return node;
    }
}

450.删除二叉搜索树中的节点

题目链接/文章讲解:代码随想录

题目链接:https://leetcode.cn/problems/delete-node-in-a-bst/description/

实现思路

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

找到需要删除的节点,删除该节点,重新构建该节点。

代码实现

class Solution1 {
    public TreeNode deleteNode(TreeNode root, int key) {
        return tracaback(root, key);
    }

    public TreeNode tracaback(TreeNode node, int key) {
        if (node == null) return null;

        // 交给对应的子树进行删除操作,获取删除后的子树
        if (node.val < key) node.right = tracaback(node.right, key);
        if (node.val > key) node.left = tracaback(node.left, key);
        // 重构当前节点
        if (node.val == key) {
            // 叶子节点替换成null节点(可以省略)
            // if (node.left == null && node.right == null) return null;

            // 右树挂载到左树最右
            if (node.left != null) {
                TreeNode temp = node.left;
                while (temp.right != null) temp = temp.right;
                temp.right = node.right;
                return node.left;
            }
            return node.right;

            // // 左树迁移到右树最左
            // if (node.right != null) {
            //     TreeNode temp = node.right;
            //     while (temp.left != null) {
            //         temp = temp.left;
            //     }
            //     temp.left = node.left;
            //     return node.right;                   
            // }
            // return node.left;
        }
        // 返回重构后的当前节点
        return node;
    }
}

669. 修剪二叉搜索树

题目链接/文章讲解:代码随想录

题目链接:https://leetcode.cn/problems/trim-a-binary-search-tree/description/

实现思路

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

二叉搜索树,有序,修剪子树,再检查当前节点是否在边界之内,返回修剪之后的节点;

代码实现

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        return traverse(root, low, high);
    }

    // 参数:当前节点,值的范围
    // 返回值:修建后的当前节点
    public TreeNode traverse(TreeNode node, int low, int high) {
        // 终止条件: 遇到空节点,没有修剪操作再需要了
        if (node == null) {return node;}

        // 单层递归逻辑:修建左右子树,修剪自己
        // 后序遍历:因为右树是可以大于当前节点本身的
        node.left = traverse(node.left, low, high);
        node.right = traverse(node.right, low, high);

        if (node.val < low) {return node.right;}
        if (node.val > high) {return node.left;}
        return node;
    }
}

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

题目链接/文章讲解:代码随想录

题目链接:. - 力扣(LeetCode)

实现思路

有序数组,转二叉平衡搜索树。按照二分查找顺序构架节点,选中间值作为节点,切割左右数组,构成子节点。

代码实现

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return traverse(nums, 0, nums.length);
    }

    // 参数:有序数组,有序数组的区间(左闭右开)
    // 返回值:组装好的节点
    public TreeNode traverse(int[] nums, int low, int high) {
        // 终止条件: 空数组,null节点
        if (low == high) {return null;}

        // 单层递归逻辑:
        int middle = (low + high) / 2;
        TreeNode node = new TreeNode(nums[middle]);

        node.left = traverse(nums, low, middle);
        node.right = traverse(nums, middle+1, high);

        return node;
    }
}

538.把二叉搜索树转换为累加树(中等)

题目链接/文章讲解:代码随想录

题目链接:https://leetcode.cn/problems/convert-bst-to-greater-tree/description/

实现思路

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

右 中 左,遍历顺序。从右子树收集和,加到当前节点,再把收集到的和送给左子树。

递归函数,参数,返回值:当前节点收集的所有和;当前节点,之前累积的和;返回收集的和。

终止条件:遇到空节点,表示该分支收集过程结束,返回收集的和

单层逻辑:先收集右子树的和,合并到当前节点,再把当前收集的和传递给左子树,最终返回收集的所有的和。

方法2:可以使用一个sum变量,记录当前收集的和,单纯的反中序遍历即可。

代码实现

class Solution {
    public TreeNode convertBST(TreeNode root) {
        traverse(root, 0);
        return root;
    }

    public int traverse(TreeNode node, int addon) {
        // 空节点,返回当前积累的值
        if (node == null) return addon;

        // 收集右子树积累的值
        int right = traverse(node.right, addon);
        // 收集右子树的积累值,合并到当前节点中
        node.val = node.val + right;
        // 把当前节点收集的值,传递到左子树中
        int left = traverse(node.left, node.val);

        // 把左节点收集的整个子树的值,返回给父节点
        return left;
    }
}

// 方法2
class Solution {
    int sum;
    public TreeNode convertBST(TreeNode root) {
        sum = 0;
        traverse(root);
        return root;
    }

    // 按右中左顺序遍历,累加即可
    public void traverse(TreeNode root) {
        if (root == null) return;
        // 收集右子树的和
        traverse(root.right);
        // 叠加当前收集的和
        sum += root.val;
        root.val = sum;
        // 收集左子树的和
        traverse(root.left);
    }
}

你可能感兴趣的:(算法)