二叉搜索树BST(分类三)


BST----Binary Search Tree (二叉搜索树、二叉查找树、排序二叉树)

概念:1.空树; 2.左右子树均是BST;3.左子树所有节点的数据域均小于或等于根节点的数据域,右子树则大于
特点:中序遍历的结果是有序的

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

力扣传送门

思路一:从根出发,两次遍历,分别记录两节点各自的路径,它们的路径必有分叉点,而那个分叉点即为最近公共祖先。例如求0,5的最近公共祖,第一次dfs获得0的路径为[6,2,0],第二次获取5的路径为[6,2,4,5]。对比两路径,可得分叉点为2.

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        List<TreeNode> lp = new ArrayList<TreeNode>();
        List<TreeNode> lq = new ArrayList<TreeNode>();
        //两次遍历,得到各自路径
        dfs(root,p,lp);
        dfs(root,q,lq);
        TreeNode ans = null;
        //对比
        for(int i=0;i<lp.size() && i<lq.size();i++){
        	//最后一个交叉点,即分叉点
            if(lp.get(i).val == lq.get(i).val) ans = lp.get(i);
            else break;
        }
        return ans;
    }    
    void dfs(TreeNode root,TreeNode find,List<TreeNode> l){
        if(root == null) return;
        if(find.val == root.val) {
            l.add(find);
            return;
        } else if(find.val < root.val) {
            l.add(root);
            dfs(root.left,find,l);
        } else {
            l.add(root);
            dfs(root.right,find,l);
        }
        return;
    }

改进:二次遍历也可改进为一次遍历,只是返回判断需要两个节点都找到了才返回

思路二:根据本题和二叉搜索树的特点,可得出左边的所有节点<根节点<右边的所有节点,说明最近公共祖先节点是介于两节点之间的。例如求0,5,其最近公共祖先为搜索树中第一个满足0<=find<=5的节点(等号是因为节点本身可做祖先),而且仅有一个(需要仔细品味)。

    TreeNode ans = null;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        int max = Math.max(p.val,q.val);
        int min = Math.min(p.val,q.val);
        dfs(root,min,max);
        return ans;
    }

    void dfs(TreeNode root, int min, int max){
        if(root == null || ans != null) return;//到底或者找到了就结束。
        else if(root.val<min) {
            dfs(root.right,min,max);//小于往右
        } else if(root.val>max){
            dfs(root.left,min,max);//大于往左
        } else {//等价root.val >= min.val && root.val <= max.val
            ans = root;
            return;
        }   
    }
236. 二叉树的最近公共祖先

力扣传送门

思路: 树递归有个特点就是无论你左边和右边的深度一不一样,当你左右两边递归结束,总会返回到同一层。例如求6和7,找到6就返回,找到7就返回,虽然两边深度不一样,但必定会在5这一层相遇,而该节点也就是答案。

注意:本题是二叉树,而非二叉搜索树,所以不具备BST的特性。

    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root == null) return null;
        //返回找到的节点
        if(root.val == p.val) return p;
        if(root.val == q.val) return q;
        //往左右递归
        TreeNode l = lowestCommonAncestor(root.left,p,q);
        TreeNode r = lowestCommonAncestor(root.right,p,q);
        //接收到左右两边返回节点都不为空,说明当前节点为答案
        if(l != null && r !=null) {
            return root;
        } 
        //否则,继续将不为空的节点返回上一层
        return l != null ? l : r;
    }
669. 修剪二叉搜索树

力扣传送门

思路:二叉搜索树–删除节点,开头说的BST概念中的第三个性质尤为重要

这种删除相对较简单,根据区间来决定—[low,high]

  • 若当前节点值在该区间内,则进入下一层递归,去判断子节点即可。
  • 若当前节点值小于low,则直接用右节点来代替该节点,也就是root = root.right,无需考虑左节点,因为当前节点一定比左节点大,也就是左节点肯定小于low,所以直接用右节点代替当前节点即可
  • 若当前节点值大于high,则用左节点来代替该节点,也就是root = root.left,理由同上;

注意:上面两步不是一次if判断赋值来解决的,而是需要通过递归或循环,因为root = root.right,可能右边的值也小于low,所以需要不断递归或循环,直到找到位于该区间内的节点才算对。

注意在java中,没有所谓的C/C++的那种指针,所以递归的时候需要返回每个节点的引用

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root == null) return null;
        if(root.val < low) root = trimBST(root.right,low,high);//递归
        else if(root.val > high) root = trimBST(root.left,low,high);//递归
        else {
            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 null;
        while(root != null){//穷举
            if(root.val < low) root = root.right;
            else if (root.val > high) root = root.left;
            else break;
        }
        if(root != null) {
            root.left = trimBST(root.left,low,high);
            root.right = trimBST(root.right,low,high);
        }
        return root;	
    }
}
230. 二叉搜索树中第K小的元素

力扣传送门

思路:开头说的BST的特点:中序遍历有序

class Solution {
    int num=0;
    int ans = -1;
    //中序遍历+获取第k个即可
    public int kthSmallest(TreeNode root, int k) {
       dfs(root,k);
       return ans;
    }
    public void dfs(TreeNode root, int k) {
        if(root == null || ans != -1) return;
        dfs(root.left,k);
        num++;
        if(k == num) {
            ans = root.val;
        }
        dfs(root.right,k);
        return;
    }
}
538. 把二叉搜索树转换为累加树

力扣传送门

思路:中序遍历的逆序
遍历顺序是8->7->6->5->4->3->2->1->0, 正好和中序遍历相反。所以遍历时只需要交换左右,然后使用一个变量用于累加每个节点值。

class Solution {
    int temp=0;//累加变量
    public TreeNode convertBST(TreeNode root) {
        if(root == null) return null;
        convertBST(root.right);
        temp += root.val;
        root.val = temp; 
        convertBST(root.left);
        return root;
    }
}
98. 验证二叉搜索树

https://leetcode.cn/problems/validate-binary-search-tree/

文章开头提到过二叉搜索树中序遍历的结果有序,那么验证是否是二叉搜索树,可根据该特性进行解题,先获取中序遍历的结果,然后两两比较,判断是否严格递增,是则true,反之false

class Solution {
    private List<Integer> arr = null;
    public boolean isValidBST(TreeNode root) {
        //中序结果有序
        // 1 5 3 4 6
        arr = new ArrayList<>();
        dfs(root);
        int l = arr.size();
        for(int i = 1; i<l ;i++) {
            if(arr.get(i-1) >= arr.get(i)) return false;
        }
        return true;
    }
    private void dfs(TreeNode root) {
        if(root == null) {
            return;
        }
        dfs(root.left);
        arr.add(root.val);
        dfs(root.right);
    }
}

优化:在遍历过程中进行比较,只要arr大小超过0就可以将当前元素和arr中的最后一个元素进行比较,如果小于说明有序,否则无序直接返回false

class Solution {
    private List<Integer> arr = null;
    private boolean ans  = false; //保存结果
    public boolean isValidBST(TreeNode root) {
        arr = new ArrayList<>();
        dfs(root);
        return !ans;
    }
    private void dfs(TreeNode root) {
        if(ans || root == null) {
            return;
        }
        dfs(root.left);
        int size = arr.size();
        //中间进行比较,逆序则无需加入,直接返回
        if(size > 0 && root.val <= arr.get(size - 1)) {
            ans = true;
            return;
        }
        arr.add(root.val);
        dfs(root.right);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值