Java~实现二叉搜索树(二叉排序树)的查找、增加、删除

概念

  • 二叉搜索树又称二叉排序树,它或是一颗空树,或者具有如下性质的二叉树
  1. 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值
  2. 若它的右子树不为空,则右子树上所有节点的值都大于根节点的值
  3. 它的左右子树也分别为二叉搜索树

在这里插入图片描述

操作-查找

  • 根据二叉搜索树的性质,我们让find指向根结点,进行循环判断,我们要找的key如果比find的key小,我们就往find的左子树找,如果key比find的key大,我们就往find的右子树找

实现

//查找
    public Node search(int key) {
        Node find = this.root;
        while (find != null) {
            if(key < find.key) {
                find = find.left;
            } else if(key > find.key) {
                find = find.right;
            } else {
                //找到了
                return find;
            }
        }
        return null;
    }

操作-增加

  • 在新增结点的过程中,由于二叉搜索树的性质,我们需要为我们要新加的结点找合适的位置,所以就会出现俩种情况
  1. 再找合适位置的时候发现此时二叉搜索树已有待插入结点的key,此时我们需要将旧的value进行覆盖。
  2. 找到我们要插入结点的父节点,此时我们需要进行判断如果待插入的key比父节点的key大,我们就是插到父结点的right,反之,插到父结点的left。

实现

//增加
    public boolean insert(int key, int value) {
        if(this.root == null) {
            //如果此时为空树
            this.root = new Node(key, value);
            return true;
        }
        Node find = root;
        Node prev = null;
        while (find != null) {
           if(key < find.key) {
               prev = find;
               find = find.left;
           } else if (key > find.key) {
               prev = find;
               find = find.right;
           } else {
               //key相等,我们使value覆盖
               find.value = value;
               return true;
           }
        }
        //结束循环,说明此时找到要插入该结点的父节点
        Node newNode = new Node(key, value);
        if(key < prev.key) {
            prev.left = newNode;
        }else {
            prev.right = newNode;
        }
        return true;
    }

假设我们要插入的数据为:

int[] array = new int[] {10,1,1,3,0,4,6,7,9};

结果便是:
在这里插入图片描述
操作-删除(难点)

  • 我们假设cur是待删除的结点,parent就是待删除结点的父结点
  1. 找到待删除结点和待删除结点的父结点。
  2. 进行分类讨论删除操作,我们主要分为三个大类第一待删除结点的左子树为空,第二待删除结点的右子树为空,第三待删除结点的左右子树都不为空。具体我们进行如下操作
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    实现
//删除
    public void delete (int key) {
        if(this.root == null) {
            return;
        }
        Node find = this.root;
        Node parent = null;
        while (find != null) {
            if(key < find.key) {
                parent = find;
                find = find.left;
            } else if (key > find.key) {
                parent = find;
                find = find.right;
            } else {
                //找到了要删除的结点
                //分为三大类,find左子树为空,右子树为空,都不为空
                if(find.left == null) {
                    //待删除结点的左子树为空,分三种结果1。是根结点 2。是parent的左结点 3.是parent的右节点
                    if(find == this.root) {
                        this.root = find.right;
                        return;
                    } else if (find == parent.left) {
                        parent.left = find.right;
                        return;
                    } else {
                        parent.right = find.right;
                        return;
                    }
                } else if (find.right == null) {
                    //带删结点的右子树为空,分三种结果1。是根结点 2。是parent的左结点 3.是parent的右节点
                    if (find == this.root) {
                        this.root = find.left;
                        return;
                    } else if (find == parent.left) {
                        parent.left = find.left;
                        return;
                    } else {
                        parent.right = find.left;
                        return;
                    }
                } else {
                    //待删除结点左右都不为空,我们找到其左子树的最小结点,与待删除结点就行赋值,然后删除最小结点
                    Node goatParent = find;
                    Node goat = find.right;
                    while (goat.left != null) {
                        goatParent = goat;
                        goat = goat.left;
                    }
                    //就行赋值
                    find.key = goat.key;
                    find.value = goat.value;
                    //删除这个最小值的结点,分俩种情况,1.goat是goatparent的左结点2.goat是goatparent的右节点
                    if(goat == goatParent.left) {
                        //那么goat的左子树一定为空
                        goatParent.left = goat.right;
                        return;
                    } else {
                        //如果goat是goatparent的右节点,那么goatparent一定还指向find结点,而且此时goat的左结点还是为空
                        goatParent.right = goat.right;
                        return;
                    }

                }
            }
        }
    }

测试


public class Test {

    public static void preOrder(BinarySearchTree.Node root) {
        if (root == null) {
            return;
        }
        System.out.print(root.key + " ");
        preOrder(root.left);
        preOrder(root.right);
    }

    public static void inOrder(BinarySearchTree.Node root) {
        if (root == null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.key + " ");
        inOrder(root.right);
    }

    public static void main(String[] args) {
        BinarySearchTree tree = new BinarySearchTree();
        int[] array = new int[] {10,1,1,3,0,4,6,7,9};
        for (int i : array
             ) {
            System.out.println(tree.insert(i, 90));
        }
        preOrder(tree.root);
        //System.out.println();
        //inOrder(tree.root);
        //System.out.println(tree.search(8).key);
        tree.delete(6);
        tree.delete(10);
        tree.delete(3);
        tree.delete(9);
        System.out.println();
        preOrder(tree.root);
    }
}

结果

true
true
true
true
true
true
true
true
true
10 1 0 3 4 6 7 9 
1 0 4 7 

性能分析

  • TreeSet TreeMap底层实现就是二叉搜索树(考虑了平衡性,红黑树)插入 、查找、删除操作的时间复杂度都是O(logN)
  • 如果没有考虑平衡性,插入查找删除就是O(N)

猜你喜欢

转载自blog.csdn.net/Shangxingya/article/details/106097296