zoukankan      html  css  js  c++  java
  • 二叉搜索树(java实现)

    二叉搜索树

    /**
     * 二叉搜索树
     */
    public class BinarySearchTree {
        public BinaryTreeNode<Integer> root;
        public int size;
    
        public BinarySearchTree() {
            this.size = 0;
        }
    
        //所谓生成二叉搜索树,就是通过n次的插入结点来完成
        public BinaryTreeNode generateBST(int[] arr){
            BinarySearchTree tree = new BinarySearchTree();
            BinaryTreeNode tmp = tree.root;
    
    
            tmp = insertNode(tmp,arr[0]);   //生成根节点,要接收新的引用
            for(int i=1;i<arr.length;i++){  //从1开始
                insertNode(tmp,arr[i]);
            }
            return tmp;
        }
    
        //二叉搜索树的插入  (整形的插入测试)
        public BinaryTreeNode insertNode(BinaryTreeNode node,int value){
    
            if(node==null){     //插入根节点
                node = new BinaryTreeNode(value);
                return node;        //返回新的引用
            }
    
            //插入的值比当前子树的根结点要小(等),往左走
            //(且要判断左孩子是否为空,为空就表示可以直接插入---因为没有左孩子,就没有比他更小的了)
            if(value<=(Integer) node.data&&node.lchild!=null){
                insertNode(node.lchild,value);
    
            }else {
                //说明左孩子为null
                if(value<=(Integer)node.data){
                    node.insertLeft(value);     //插入左孩子
                }else if(value>(Integer)node.data&&node.rchild!=null){
                    insertNode(node.rchild,value);
                }else{
                    //说明右孩子为null
                    node.insertRight(value);    //插入右孩子
                }
            }
            return null;
        }
    
        //层序遍历
        public void levelTraverse() throws Exception {
            BinaryTreeNode node = root;
    
            CycleQueue queue = new CycleQueue(new BinaryTreeNode[100]);
            queue.enQueue(node);
    
            while (!queue.isEmpty()){
                BinaryTreeNode current = (BinaryTreeNode) queue.deQueue();
                System.out.printf("%5d",current.data);
    
                if(current.lchild!=null){
                    queue.enQueue(current.lchild);
                }
                if(current.rchild!=null){
                    queue.enQueue(current.rchild);
                }
            }
        }
    }
    

    测试:

            /**
             *  二叉搜索树
             */
    
            BinarySearchTree searchTree = new BinarySearchTree();
            int array[] = {50,24,76,4,32,64,100};
            int array2[] = {20,40,10,80,45,60,12};
    
           searchTree.root =  searchTree.generateBST(array2);
    
            System.out.println();
            System.out.println("层序遍历二叉搜索树:");
            searchTree.levelTraverse();
        }
    

    总结:

    简单来说:从根节点出发,往哪里走的问题

    插入结点,生成树其实就是不断的插入而成

    loop(node,value):

    1. 当比根节点大(往右走)
      1. 往右走如果右孩子为空,则直接插入作为右孩子
      2. 如果右孩子不为空,则递归进右孩子处goto loop(node.rchild,value)。继续做根节点的左右大小比较(决定往哪走)
    2. 比根节点小(往左走)
      1. 往左走若左孩子为空,则直接插入作为左孩子
      2. 如果左孩子不为空,则递归进左孩子goto loop(node.lchild,value)处。把这个左孩子当作根节点,继续做左右大小比较(决定下一步往哪走)

    查找操作

        //二叉搜索树的结点查找,判断是否存在
        public boolean findNode(BinaryTreeNode node,int value){
            //比node.data小,往左走,且判断左孩子是否为空
            
            if(value<(Integer)node.data&&node.lchild!=null){
                return findNode(node.lchild,value);
                
                //表示由于左孩子为空而退出的if,且value仍然<node.data,所以表示找不到指定结点
            }else if(value<(Integer)node.data){
                return false;
            }else if(value>(Integer)node.data&&node.rchild!=null){
                return findNode(node.rchild,value);
            }else if(value>(Integer)node.data){
                return false;
            }else{
                //当上面的情况都不符合,就表示属于value==node.data的情况,所以表示找到了,返回true
                return true;
            }
        }
    

    时间复杂度:O(log2n)

    删除操作

    分析:

    删除操作分三种情况

    1. 删除的结点无左,右孩子------可直接删除该结点(比如parent.lchild=null即可,java中无需手动释放资源,C和python都要释放)
    2. 删除的结点只有左或者右孩子中的一个---------首先判断删除节点是来自父节点的左孩子,还是右孩子。
      1. 被删除的结点A是左孩子,且A结点只有左孩子,那么--- parent.lchild = A.lchild
      2. 如果被删除结点B是右孩子,且B结点只有右孩子,那么---parent.rchild = B.rchild
    3. 删除的结点既有左孩子,又有右孩子-----做法是找到被删除的结点C,的右子树中最小的结点值min_value
      1. C.value = min_value 替换值
      2. 根据这个min_value 在C.rchild为根结点的子树中,删除(这个删除也就是我们定义的删除操作,递归式的)这个min_value所在的结点
        //二叉搜索树的删除指定结点
        public boolean deleteNode(BinaryTreeNode node,int value,BinaryTreeNode parent){
            /**
             * 首先找出该结点以及记录下他的双亲节点parent
             *  1. 第一种情况:为叶子节点, 通过parent直接删除该节点即可 parent.lchild = null
             *  2. 第二种情况,删除的结点有左孩子,或者右孩子,通过 如parent,parent.lchild = node.lchild,删除
             *  3. 第三种情况,删除的结点有左孩子,也有右孩子,通过找到这个结点右子树中最小的结点(find_right_min)替代掉被删除的结点,且这个最小的结点要置为null
             */
    
            if(value<(Integer)node.data&&node.lchild!=null){
               return  deleteNode(node.lchild,value,node);
            }else if(value<(Integer)node.data){
                return false;       //表示没有这个结点
            }else if(value>(Integer)node.data&&node.rchild!=null){
               return  deleteNode(node.rchild,value,node);
            }else if(value>(Integer)node.data){
                return false;
            }else{
                //表示value == node.data,存在该结点
    
                //1. 左右孩子都不存在
                if(node.lchild==null&&node.rchild==null){
                    //1.1 被删除节点是父节点的左孩子
                    if(node==parent.lchild){
                        parent.lchild = null;
                        return true;
                        //1.2 被删除节点是父节点的右孩子
                    }else{
                        parent.rchild = null;
                        return true;
                    }
                    //2. 左孩子存在,右孩子不在
                }else if(node.lchild!=null&&node.rchild==null){
                    //2.1 当前结点是父节点的左孩子
                    if(node==parent.lchild){
                        parent.lchild = node.lchild;
                        return true;
                        //2.2 当前节点是父节点的右孩子
                    }else{
                        parent.rchild = node.lchild;
                        return true;
                    }
                    //3. 右孩子存在,左孩子不在
                }else if(node.lchild==null&&node.rchild!=null){
                    //2.1 当前结点是父节点的左孩子
                    if(node==parent.lchild){
                        parent.lchild = node.rchild;
                        return true;
                        //2.2 当前结点是父节点的右孩子
                    }else{
                        parent.rchild = node.rchild;
                        return true;
                    }
                    //4. 左孩子,右孩子都在  node.lchild!=null&&node.rchild!=null
                }else{
                    //4.1 找到右子树中最小的结点值
                    Integer min_value = find_min_value(node.rchild);
                    //4.2 替换最小值
                    node.data = min_value;
                    //4.3 删除右子树中的最小节点
                    deleteNode(node.rchild,min_value,node);
                    return true;
                }
            }
        }
    

    查找最小值

        //内部方法,用于查找最小结点值
        private Integer find_min_value(BinaryTreeNode node){
            if(node==null){
               return null;
            }
            if(node.lchild==null){
                return (Integer)node.data;
            }else{
                return find_min_value(node.lchild);
            }
        }
    

    测试:

            int array3[] = {50,30,80,20,35,70,100,34,40,75,32,36};
    
    
            BinarySearchTree st = new BinarySearchTree();
    
            //注意,一定要接收这个返回的副本引用!!!
            st.root = st.generateBST(array3);
    
            System.out.println();
            System.out.println("bst:");
            st.levelTraverse();
    
            //测试删除结点
            System.out.println();
            System.out.println("删除35结点: ");
            st.deleteNode(st.root,35,st.root);
            st.levelTraverse();
    

    测试结果:

    原树:

    删除35后:

    ​ 50

    ​ 30 80

    ​ 20 36 70 100

    ​ 34 40 75

    ​ 32

    参考链接:https://zhuanlan.zhihu.com/p/30918614

  • 相关阅读:
    EF Code First一对一、一对多、多对多关联关系配置
    ASP.NET Core 运行原理剖析2:Startup 和 Middleware(中间件)
    ASP.NET Core 运行原理剖析1:初始化WebApp模版并运行
    ASP.NET Core 1.0 入门——Application Startup¶
    ASP.NET Core管道深度剖析(1):采用管道处理HTTP请求
    EF Code First 学习笔记:约定配置
    ASPNET CORE初探
    EntityFramework 7.0之初探【基于VS 2015】(
    ASP.NET Core 开发
    sqlite3 外键作用原理和使用方法
  • 原文地址:https://www.cnblogs.com/zhanp/p/10932688.html
Copyright © 2011-2022 走看看