0
点赞
收藏
分享

微信扫一扫

【二分搜索树BST】

浮游图灵 2022-04-03 阅读 37

目录

概念

向BST中添加一个元素​

使用 debug 来查看结果:

覆写 toString 方法查看结果:

查找二分搜索树中是否包含值val

在BST中查找最值

删除操作

1.删最值

2.删除任意值


概念

BinarySeachTree,是具有以下性质的二叉树

  • 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  • 若它的右子树不为空,则右子树.上所有节点的值都大于根节点的值
  • 它的左右子树也分别为二叉搜索树

总而言之就是:左子树中的所有节点值<根节点<右子树的所有节点值

二分搜索树中一般不考虑值相等的情况(元素不重复),JDK中的搜索树就不存在相同的值(TreeMap-key)

对二分搜索树进行元素的查找过程实际上就是一个二分查找,所以在进行查找时的效率是非常高的,它的时间复杂度是 logN 级别的。

向BST中添加一个元素

 可以看到,新添加的元素一定是作为叶子节点存在

代码实现:

//基于整型的普通二分搜索树
public class BST {
    private class TreeNode{
     int val;
     TreeNode left;
     TreeNode right;
     public TreeNode(int val){
         this.val = val;
     }
    }
    private TreeNode root;
    private int size;
    //插入节点
    public  void add(int val){
        root = addNode(root,val);
    }
    //向以root为根节点的BST中插入值为val的节点
    public TreeNode addNode(TreeNode root,int val){
        TreeNode newNode = new TreeNode(val);
        if(root == null){
            size ++;
            return newNode;
        }
        if(val < root.val){
            //在左子树插入
            root.left = addNode(root.left,val);
        }else {
            //在右子树插入
            root.right = addNode(root.right,val);
        }
        return root;
    }
}
public class BSTTest {
    public static void main(String[] args) {
        BST bst = new BST();
        int[] arr = {28,16,30,13,22,29,42};
        for (int i : arr){
            bst.add(i);
        }
        System.out.println();
    }
}

使用 debug 来查看结果:

覆写 toString 方法查看结果:

    @Override
    public String toString() {
       StringBuffer sb = new StringBuffer();
       generaBSTString(root,0,sb);
       return sb.toString();
    }
    //前序遍历以root为根节点的BST中,将基础存储在sb中
    private void generaBSTString(TreeNode root, int height, StringBuffer sb) {
        if(root == null){
            sb.append(generatHeightStr(height)).append("NULL\n");
            return;
        }
        sb.append(generatHeightStr(height)).append(root.val).append("\n");
        //递归访问左子树
        generaBSTString(root.left,height+1,sb);
        //递归访问右子树
        generaBSTString(root.right,height+1,sb);
    }
   //按照当前所处的树的层次打印 -- ,每多一层,就多两个 --
    private String generatHeightStr(int height) {
        StringBuffer sb = new StringBuffer();
        for(int i = 0;i < height;i++){
            sb.append("--");
        }
        return sb.toString();
    }
public class BSTTest {
    public static void main(String[] args) {
        BST bst = new BST();
        int[] arr = {28,16,30,13,22,29,42};
        for (int i : arr){
            bst.add(i);
        }
        System.out.println(bst);
    }
}

输出结果: 

可以看到,16和30是第二层,所有有一组“--”,处在第三层的有两组“--”,所有generatHeightStr方法就是当进入子树时,高度就加一,就多打印一组“--”。

查找二分搜索树中是否包含值val

    //查找树中是否包含值val
    public boolean contains(int val){
        return contains(root,val);
    }
    //判断以root为根节点的树中是否包含值val
    private boolean contains(TreeNode root, int val) {
        if(root == null){
            return false;
        }
        if(root.val == val){
            return true;
        }else if (val < root.val){
            //当前值小于根,去左树找
           return contains(root.left,val);
        }else {
          return   contains(root.right,val);
        }
    }
    public static void main(String[] args) {
        BST bst = new BST();
        int[] arr = {28,16,30,13,22,29,42};
        for (int i : arr){
            bst.add(i);
        }
        System.out.println(bst.contains(29));
        System.out.println(bst.contains(100));
    }
//输出:true
        false

在BST中查找最值

对于任意一颗二分搜索树,它的最小值一定在左树的最左侧。但是最小值一定在树的叶子节点吗,如下图的第二棵树,显然16不是叶子节点,所以,这句话不对。

根据BST的性质,可以得出,不断向左树递归查找,找到的第一个左子树为空的节点一定是最小值。

同理,在BST中找最大值,一定在右树的最右侧,也就是不断向右树递归查找,找到的第一个右子树为空的节点一定是最大值。

  代码实现:

    //找最小值
    public int findMinVal(){
        if(size == 0){
            throw new NoSuchElementException("BST is empty!");
        }
        TreeNode minNode = finMinNode(root);
        return minNode.val;

    }
    //找到以root为根节点的树的最小值
    private TreeNode finMinNode(TreeNode root) {
        if(root.left == null){
            return root;
        }else {
            return finMinNode(root.left);
        }
    }
    //找最大值
    public int findMaxVal(){
        if(size == 0){
            throw new NoSuchElementException("BST is empty!");
        }
        TreeNode maxNode = findMaxNode(root);
        return maxNode.val;
    }
    //找到以root为根节点的树的最大值
    private TreeNode findMaxNode(TreeNode root) {
        if(root.right == null){
            return root;
        }else {
            return findMaxNode(root.right);
        }
    }
    public static void main(String[] args) {
        BST bst = new BST();
        int[] arr = {28,16,30,13,22,29,42};
        for (int i : arr){
            bst.add(i);
        }
        System.out.println("最小值为:"+bst.findMinVal());
        System.out.println("最大值为:"+bst.findMaxVal());
    }
//输出:最小值为:13
       最大值为:42

删除操作

1.删最值

    //删除最小值,并且将该值返回
    public int removeMin(){
        int min = findMinVal();
        root = removeMin(root);
        return min;
    }
    //在当前以root为根的树中删除最小值节点,返回更新后的树根
    private TreeNode removeMin(TreeNode root) {
        if(root.left == null){
            //此时根节点就是最小值
            TreeNode rightNode = root.right;
       //   root.right = root = null;
            root.right = null;
            root = null;
            size --;
            return rightNode;
        }
        //去左子树中删除
        root.left = removeMin(root.left);
        return root;
    }
    public static void main(String[] args) {
        BST bst = new BST();
        int[] arr = {28,16,30,13,22,29,42};
        for (int i : arr){
            bst.add(i);
        }
        bst.removeMin();
        System.out.println(bst);
    }

输出结果:

root.right = null是为了删除16右子树的那根线,root =null为了让JVM删除16这个节点 

以下面的二分搜索树为例,画出递归图,便于我们对代码的实现加深了解。

 删除最大值和最小值的方法思路一致,读者可以自己去实现以下。

2.删除任意值

删除任意值时有下面这三种情况:

  1. 要删除的节点只有左子树
  2. 要删除的节点只有右子树
  3. 要删除的节点左右子树都有

 

 

 代码实现:

    //删除任意值
    public void remove(int val){
        root = removeVal(root, val);
    }
    //删除以root为根节点的树中的val值,返回删除后的新的根节点
    private TreeNode removeVal(TreeNode root, int val) {
        if(!contains(val)){
            //树中不包含Val
            throw new NoSuchElementException("BST中没有值为"+val +"val的节点");
        }else if(root.val == val){
            if(root.right == null){
                //右子树为空,将左子树作为根节点
                TreeNode leftNode = root.left;
                root.left = root = null;
                size--;
                return leftNode;
            }else if ( root.left == null){
                //左子树为空,将右子树作为根节点
                TreeNode rightNode = root.right;
                root.right = root = null;
                size --;
                return rightNode;
            }
               //此时说明左右都不为空,去找右子树第一个大于根节点的节点
              //也就是找以右子树为根节点的树的最小值
                TreeNode prev = finMinNode(root.right);
               //先拼接右子树
                prev.right = removeMin(root.right);
                //再拼接左子树
                prev.left = root.left;
                //删除节点root
                root.left = root.right = root = null;
                return prev;
        }else if(val < root.val){
            //去左子树找
            root.left = removeVal(root.left,val);
            return root;
        }else {
            //此时val > root.val,去右子树走
            root.right =  removeVal(root.right,val);
            return root;
        }
    }
    public static void main(String[] args) {
        BST bst = new BST();
        int[] arr = {41,58,50,60,42,53,59,63};
        for (int i : arr){
            bst.add(i);
        }
        bst.remove(58);
        System.out.println(bst);

    }
举报

相关推荐

0 条评论