LeetCode刷题记-二叉树-二叉搜索树

700. 二叉搜索树中的搜索


题目:

给定二叉搜索树(BST)的根节点 root 和一个整数值 val。
你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

写题历程:

  • 这道题只要抓住二叉搜索树的特性就很好做了,左边比根节点值小,右边比根节点值大,剩下的搜索,只是一些判断,然后递归搜索就行了


解法:

class Solution {
    public TreeNode searchBST(TreeNode root, int val) {
        if(root == null) return null;
        if(root.val == val) return root;
        if(root.val > val) {
            return searchBST(root.left,val);
        }
        if(root.val < val) {
            return searchBST(root.right,val);
        }
        return null;
    }
}

98. 验证二叉搜索树


题目:

给你一个二叉树的根节点 root ,判断其是否是一个有效的二叉搜索树。

有效 二叉搜索树定义如下:

节点的左子树只包含 小于 当前节点的数。
节点的右子树只包含 大于 当前节点的数。
所有左子树和右子树自身必须也是二叉搜索树。

写题历程:

  • 一开始掉坑里了,只比较了左节点和右节点与当前节点的情况,但是!

  • 二叉搜索树不是只对左右节点做要求呀,而是左右子树,是子树

  • 或许左右节点确实满足条件了,但是左右节点的子节点呢,怕是不一定吧

  • 在我意识到这个之后(其实是看题解才发现我掉坑了),思考怎么整...

  • 我们可以用一个变量去记录当前节点值,采用中序遍历的方式,先递归左子树,若当前节点值大于左结点值,记录当前节点值,再去递归右子树,最后左右子树相与返回


解法:

class Solution {
    Long maxVal = -2147483649L;
    public boolean isValidBST(TreeNode root) {
        if(root == null) return true;
        boolean leftTree = isValidBST(root.left);   
        if(!leftTree) return false;
        if(root.val <= maxVal) return false;
        maxVal = Long.valueOf(root.val);
        boolean rightTree = isValidBST(root.right);
        return leftTree && rightTree;
    }
}

530. 二叉搜索树的最小绝对差


题目:

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

差值是一个正数,其数值等于两值之差的绝对值。

写题历程:

  • 利用二叉搜索树的特性,根据中序遍历,使之有序,再来查找最小绝对差值

  • 如何找最小绝对差值?第一,用一个变量记录答案,第二,用一个变量记录当前节点的上一节点的值,这样我们就可以计算差值,将差值更新到答案上,直至找到最小


解法:

class Solution {
    Integer res = Integer.MAX_VALUE;
    Integer pre = null; //记录前一个节点的值
    public int getMinimumDifference(TreeNode root) {
        if(root == null) return 0;
        int left = getMinimumDifference(root.left);
        if(pre != null) {
            res = Math.min(res,root.val - pre);
        }
        pre = root.val;
        int right = getMinimumDifference(root.right);
        return res;
    }
}

501. 二叉搜索树中的众数


题目:

给你一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有 众数(即,出现频率最高的元素)。
​
如果树中有不止一个众数,可以按 任意顺序 返回。
​
假定 BST 满足如下定义:
​
    1. 结点左子树中所含节点的值 小于等于 当前节点的值
    2. 结点右子树中所含节点的值 大于等于 当前节点的值
    3. 左子树和右子树都是二叉搜索树

写题历程:

  • 二叉搜索树的重要性质:中序遍历后是一个非递减序列

  • 因此问题简化成,在一个有序非递减数组中找出现频率最高的数

  • 但是写的过程中,代码还是差点意思,考虑的情况不周

  • 不仅要比较当前节点与上一个节点的值,还需要找到最高频率

  • 通过多重判断,最后将集合转换成数组。思路上确实不难,但实现起来步骤还是有些许繁琐的


解法:

class Solution {
        List list = new ArrayList<>();
        Integer pre = null;    //记录上一个节点的值
        int maxFrequency = 0;    //最大频率
        int frequency = 0;    //当前节点的出现频率
    public int[] findMode(TreeNode root) {
        searchMode(root);
        return list.stream().mapToInt(Integer::intValue).toArray();

    }   
    public void searchMode(TreeNode node) {
        if(node == null) return;
        searchMode(node.left);
        if(pre != null && pre == node.val) frequency ++;    
        else frequency = 1;    //如果不相同,频率只能为1,因为只出现一次
        if(frequency > maxFrequency) {
            maxFrequency = frequency;    //更新最大频率
            list.clear();    //加入集合的元素应该移除,因为有频率更高的数了
            list.add(node.val);
        } else if(frequency == maxFrequency) {
            list.add(node.val);
        }
        pre = node.val;
        searchMode(node.right);
    }
}

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


题目:

给定一个二叉搜索树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
​
例如,给定如下二叉搜索树:  root = [6,2,8,0,4,7,9,null,null,3,5]

写题历程:

  • 利用好搜索树的性质,左边小,右边大

  • 那就可以比较根节点与左右子树节点的大小关系

  • 均大于,递归左边;均小于,递归右边

  • 在之间,则返回


解法:

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        //经过中序遍历可以将二叉搜索树变成一个有序数组,找两个节点的最近公共祖先
        //相当于在数组中[p,q]可以找到该最近公共祖先
        //如何在数组中准确找到?如何排除其他元素?
        if(root == null) return null;

        if(root.val > p.val && root.val > q.val) {
            return lowestCommonAncestor(root.left,p,q);
        }
        if(root.val < p.val && root.val < q.val) {
            return lowestCommonAncestor(root.right,p,q);
        }
        return root;
    }
}

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


题目:

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

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。

写题历程:

  • 有一说一,这题我思路来的挺快的,只是对于回溯的领悟还有点差距,回溯条件想错了

  • 看了点提示后,再自己人工debug了下,弄清楚了

  • 这种带有性质的树其实反而不用那么担心,正是因为特殊,而恰好能利用它的性质去解决

  • 例如本题插入节点,我们只需判断下当前节点的值与要插入的值的大小关系,当前节点值大,则插到左边,反之右边,然后递归,遇到空就回溯,然后直接插入就行了


解法:

class Solution {
    public TreeNode insertIntoBST(TreeNode root, int val) {
        return traversal(root,val);
    }
    public TreeNode traversal(TreeNode root, int val) {
        if(root == null) return new TreeNode(val);  //如果是空树,直接用插入的节点创建一棵树
        if(root.val > val) {
            TreeNode left = traversal(root.left,val);
            root.left = left;   //回溯
        }
        if(root.val < val) {
            TreeNode right = traversal(root.right,val);
            root.right = right; //回溯
        }
        return root;
    }
}

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


题目:

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。
​
一般来说,删除节点可分为两个步骤:
首先找到需要删除的节点;
如果找到了,删除它。

写题历程:

  • 我本以为这题和在二叉搜索树中插入节点的思路差不多,没想到它需要考虑的情况这么多

  • 首先最主要的是,删除节点会修改二叉搜索树的结构,即你删除完节点后,还需要调整为一颗二叉搜索树

  • 分五种情况:

  • 未找到目标节点,即根节点为空

  • 找到目标节点

  • 目标节点是叶子节点

  • 目标节点的左子树为空,右子树不为空

  • 目标节点的左子树不为空,右子树为空

  • 目标节点的左右子树均不为空

  • 具体理一下 目标节点的左右子树均不为空 的思路:

  • 当目标节点的左右子树均不为空,且需要删除目标节点,首先目标节点的父节点必定要连接目标节点的子节点

  • 其次,连接完毕后,还得保证这棵树仍然是一棵二叉搜索树

  • 我们可以把目标节点的左右子树进行合并,再调整结构,最后与目标节点的父节点进行连接

  • 具体实现:(将左子树合并到右子树)

  • 利用一个临时节点指向目标节点的右子节点

  • 遍历这棵子树找到此树的最左叶子节点

  • 最左叶子节点去连接目标节点的左子树,即完成合并

  • 最后一步,将目标节点删除,即目标节点的右节点成为新的根节点

  • 当然,将右子树合并到左子树也是ok的,把里面左右修改下就行,答案不唯一


解法:

class Solution {
    public TreeNode deleteNode(TreeNode root, int key) {
        if(root == null) return root;    //第一种情况
        if(root.val == key) {   //找到目标
            if(root.left == null) return root.right;    //这两行包含了二三四的情况
            else if(root.right == null) return root.left;
            else {    //第五种情况
                TreeNode cur = root.right;
                while(cur.left != null) {
                    cur = cur.left;
                }
                cur.left = root.left;
                root = root.right;
                return root;
            }
        }
        if(root.val > key) root.left = deleteNode(root.left,key);
        if(root.val < key) root.right = deleteNode(root.right,key);
        return root;
    }
}

669. 修剪二叉搜索树


题目:

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

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

写题历程:

  • 我试着用删除节点的思路来解这题,其实还是可行的,就是遍历的顺序发生了变化,若用上题的思路,则需要后序遍历

  • 其实先序遍历也可以搞定,反而会简单,只需要判断到当前节点的值不在范围内,再根据二叉搜索树的性质,左子树小,右子树大,即可以剪掉其某一边的子树,去递归另一边


解法:

/**
    先序
**/
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root == null) return root;
        if(root.val < low) return trimBST(root.right,low,high);    //左子树会更小,返回右边
        if(root.val > high) return trimBST(root.left,low,high);    //右子树会更小,返回左边
        root.left = trimBST(root.left,low,high);    //上下层连接
        root.right = trimBST(root.right,low,high);    //上下层连接
        return root;
    }
}

/**
    后序
**/
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root == null) return root;
        root.left = trimBST(root.left,low,high);
        root.right = trimBST(root.right,low,high);
        //不满足条件
        if(root.val < low || root.val > high) {
            if(root.left == null) return root.right;
            else if(root.right == null) return root.left;
        }
        return root;
    }
}

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


题目:

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

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

写题历程:

  • 前面反复提过,二叉搜索树的一个重要性质:中序遍历将变成一个非递减序列

  • 而这一题不就恰好反过来而已吗

  • 我们可以根据上述性质知道,数组中间的元素就是头节点,然后以此分割,左边的在左子树,右边的在右子树,递归一下即可

  • 还有注意分割的时候,要遵循 循环不变量原则


解法:

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return traversal(nums,0,nums.length);
    }
    public TreeNode traversal(int[] nums, int start,int end) {
        if(start == end) return null;
        int index = start + end >> 1;
        TreeNode root = new TreeNode(nums[index]);
        if(root == null) return root;
        //左闭右开
        root.left = traversal(nums,start,index);
        root.right = traversal(nums,index + 1, end);
        return root;
    }
}

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


题目:

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

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

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

写题历程:

  • 一开始没看懂题,小小地绕了一下,不过可以看出它有点像是反过来的二叉搜索树(个人的理解),即根节点的右边小,左边大

  • 照着这个思路,再根据二叉搜索树的重要性质,可以知道它的中序遍历也是反的,也就是右中左,再就是累加树。至此,题目算是理清了

  • 我们从从右到左递归遍历再累加,最后返回根节点,就可以搞定这题了

  • 具体看代码...


解法:

class Solution {
    Integer sum = 0;    //累加器
    public TreeNode convertBST(TreeNode root) {
        convert(root);
        return root;
    }
    public void convert(TreeNode root) {
        if(root == null) return ;
        convert(root.right);    //右
        
        //中
        sum += root.val;    //每多遍历一个节点,则多累加一个节点的值
        root.val = sum;    //将当前节点的值更新为累加值
        
        convert(root.left);    //左
    }
}

你可能感兴趣的:(LeetCode刷题记,leetcode,算法,数据结构)