数据结构:二叉查找树的api设计及其java代码实现

关于二叉树的一些基本概念

二叉树:
二叉树就是度不超过2的树(每个结点最多有两个子结点)

满二叉树:
一个二叉树,如果每一层的结点数都达到最大值,则这个二叉树就是满二叉树

完全二叉树:
叶结点只能出现在最下层和此下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树,即如果树不满则要求左满右不满,是为完全二叉树

 

我们分析了二叉树结点的特性后,不难设计出二叉树的结点类 。

 private class Node {
        private K key;//键
        private V value;//值
        private Node left;//左子结点
        private Node right;//右子结点

        public Node(K key,V value,Node left,Node right){
           this.key=key;
           this.value=value;
           this.left=left;
           this.right=right;
        }

    }

下面,我们接着来分析二叉查找树的API设计

类名: BinaryTree<K extends Comparable<K>,V value>
成员变量: 
 private Node root  记录根结点
 private int N 记录树中元素个数
成员方法:
 public void put(K key,V value) 向树中插入一个键值对
 private  Node put(Node,K key,V value) 向树中指定结点处插入键值对
 public V  get(K key) 查找树中key对应值
 private V get(Node x,K key) 从树中指定节点x中查key对应值
 public void delete(K key) 根据key删除键值对
 private Node delete(Node x,K key) 根据key删除树中指定结点处的键值对
 public int size() 获取树中元素个数  
  

BinaryTree 完整的java代码实现

package com.tingcream.alg.tree;


import com.tingcream.alg.linear.Queue;

/**
 * 二分查找树类,特性
 * 1、每个结点最多有两个子节点(左子节点、右子节点)
 * 2、左子节点存储的元素比当前节点小,右子节点存储的元素比当前节点大
 *
 * BinaryTree
 *
 */
public class BinaryTree<K extends Comparable<K>,V> {
    private Node root;//表示树的根结点
    private int N;//记录数中元素个数


    //获取数量
    public int size(){
        return this.N;
    }

    public boolean isEmpty(){
        return this.N<=0;
    }

    //向树中put一个元素key-value
    public void put(K key, V value){
        //调用重载的put方法
        root=put(root,key,value);
    }

    //向指定的结点x中添加kv,返回添加后的x结点
    public Node put(Node x,K key, V value){
        //如果x为空,则new一个新的结点作为根结点
        if(x==null){
            N++;
            return new Node(key,value,null,null);
        }
        //如果x不为空
        // 比较x结点的键和key的大小
        //如果key小于 x结点的键,则继续找x结点的左子树
        //如果key大于 x结点的键,则继续找x结点的右子树
        //如果key等于x 结点的键,则替换x结点的值为value
        int cmp=key.compareTo(x.key);
        if(cmp>0){
            x.right= put(x.right,key,value);

        }else if(cmp<0){
            x.left=put(x.left,key,value);
        }else{//key与当前结点x的键相等,则替换x结点的value值
            x.value=value;
        }
        return x;

    }




    //查询树中指定key对应的value
    public  V get(K key){
        return  get(root,key);
    }

    //从指定的结点x中,查找key对应的值
    public V get(Node x,K key){
        //如果结点x为null
        if(x==null){
            return null ;
        }
        int cmp=key.compareTo(x.key);
        if(cmp>0){
           // 如果key大于 x结点的键,则继续找x结点的右子树
            return  get(x.right,key);
        }else if(cmp<0){
            //如果key小于 x结点的键,则继续找x结点的左子树
            return  get(x.left,key);
        }else{
            //如果key等于x 结点的键,则找到了键为key的结点,返回这个结点的value即可
            return  x.value;
        }
    }




    //删除树中key对应的结点
    public void delete(K key){
       delete(root,key);
    }

    //删除指定结点x中的key对应的结点,并返回被删除的结点x
    public Node delete(Node x,K key){
        //x树为null
        if(x==null){
            return null;
        }
        //x树不为null
        int cmp=key.compareTo(x.key);
        if(cmp>0){
            // 如果key大于 x结点的键,则继续找x结点的右子树
            x.right=delete(x.right,key);
        }else if(cmp<0){
            //如果key小于 x结点的键,则继续找x结点的左子树
            x.left=delete(x.left,key);
        }else{

            //如果key等于x结点的键,则找到了我们要删除的节点x

            //元素个数减一
            N--;

            //如果x没有右子节点,那么直接返回左节点
            if(x.right==null){
                return x.left;
            }

            //x有右子节点,没有左边子节点,返回右子节点
            if(x.left==null ){
                return x.right;
            }


            //结点x既有左子结点又有右子结点

            //找到右子树中的最小结点
            if(x.right.left==null){
                x.right.left=x.left;
                x=x.right;
               return x;
            }else{
                Node minNode= x.right;
                while(minNode.left!=null){
                    minNode= minNode.left;
                }

                //删除右子树中最小的结点
                Node n=x.right;
                while(n.left!=null){
                    if(n.left.left==null){
                        n.left=null; //删除右子树中最小的结点 ,断开left引用
                    }else{
                        n=n.left;
                    }
                }


                //交换x和minNode节点
                //            //让x结点的左子树成为minNode的左子树
//            //让x结点的右子树成为minNode的右子树
//                minNode.left=x.left;
//                minNode.right=x.right;
//                //让x结点的父亲结点指向minNode
//                x=minNode;

                //x结点不动,用minNode结点中的key、value替换x节点中的key、value
                x.key=minNode.key;
                x.value=minNode.value;

            }

        }

        return x;
    }





    //查找二叉查找树中的最小的键
    public K  min(){
        if(root==null){
            return null;
        }
        return min(root).key;
    }
    //找出最小键所在结点
    private Node min(Node x){
        //方式二  :while循环
        Node n=x;
        while(n.left!=null){
            n=n.left;
        }
        return n;

    }



    //查找二叉查找树中的最大的键
    public K  max(){
        if(size()>0){
            return null;
        }
        return max(root).key;
    }

    //找出最大键所在结点
    private Node max(Node x){
        //方式二  :while循环
        Node n=x;
        while(n.right!=null){
            n=n.right;
        }
        return n;
    }


    //使用前序遍历,获取整个树中的所有的键
    public Queue<K> preErgodic(){
        Queue<K> keys= new Queue<K>();//new一个空队列keys
        preErgodic(root,keys);
        return keys;
    }

    //使用前序遍历,把指定树x中的所有键放入到keys队列中
    private  void preErgodic(Node x,Queue<K> keys){
        //如果x树为空,则直接return (安全性校验)
        if(x==null){
            return;
        }
        //把x结点的key放入到keys中
        //递归遍历x结点的左子树
        //递归遍历x结点的右子树

        //将x树的根结点入队
        keys.enqueue(x.key);

        //如果x树有左子树,则继续遍历它的左子树
        if(x.left!=null){
            preErgodic(x.left,keys);
        }

        //如果x树有右子树,则继续遍历它的右子树
        if(x.right!=null){
            preErgodic(x.right,keys);
        }
    }




    //使用中序遍历,获取整个树中的所有的键
    public Queue<K> midErgodic(){
        Queue<K> keys= new Queue<K>();//new一个空队列keys
        midErgodic(root,keys);
        return keys;
    }

    //使用中序遍历,把指定树x中的所有键放入到keys队列中
    private  void   midErgodic(Node x,Queue<K> keys){
        //如果x树为空,则直接return (安全性校验)
        if(x==null){
            return;
        }
        //把x结点的key放入到keys中
        //递归遍历x结点的左子树
        //递归遍历x结点的右子树

        //如果x树有左子树,则继续遍历它的左子树
        if(x.left!=null){
            midErgodic(x.left,keys);
        }

        //将x树的根结点入队
        keys.enqueue(x.key);

        //如果x树有右子树,则继续遍历它的右子树
        if(x.right!=null){
            midErgodic(x.right,keys);
        }
    }




    //使用后序遍历,获取整个树中的所有的键
    public Queue<K> afterErgodic(){
        Queue<K> keys= new Queue<K>();//new一个空队列keys
        afterErgodic(root,keys);
        return keys;
    }

    //使用后序遍历,把指定树x中的所有键放入到keys队列中
    private  void   afterErgodic(Node x,Queue<K> keys){
        //如果x树为空,则直接return (安全性校验)
        if(x==null){
            return;
        }
        //把x结点的key放入到keys中
        //递归遍历x结点的左子树
        //递归遍历x结点的右子树

        //如果x树有左子树,则继续遍历它的左子树
        if(x.left!=null){
            afterErgodic(x.left,keys);
        }

        //如果x树有右子树,则继续遍历它的右子树
        if(x.right!=null){
            afterErgodic(x.right,keys);
        }

        //将x树的根结点入队
        keys.enqueue(x.key);
    }

    //层序遍历,遍历整个树所有key加入队列  (广度优先 )
    public Queue<K>  layerErgodic(){
        if(isEmpty()){
            return null;
        }

        //定义两个队列,分别存储树中的key和树中的节点
        Queue<K> keys=new Queue();//要返回的keys 队列
        Queue<Node> nodes=new Queue();//节点队列

        //向把root结点放入nodes队列
        nodes.enqueue(root);
        while(!nodes.isEmpty()){

            //出队一个结点,得到其key放入keys队列
            Node n= nodes.dequeue();
            keys.enqueue(n.key);

            //判断新出队的节点是否有左子节点,如果有则将其左子节点入队
            if(n.left!=null){
                nodes.enqueue(n.left);
            }
            //判断新出队的节点是否有右子节点,如果有则将其右子节点入队
            if(n.right!=null){
                nodes.enqueue(n.right);
            }
        }

        return  keys;
    }



    //获取整个树最大深度
    public int maxDepth(){
        if(isEmpty()){
           return 0;
        }
        return  maxDepth(root);

    }
    //获取指定树的最大深度
    private int maxDepth(Node x){
        if(x==null){
            return 0;
        }

        //获取x结点的左子树的最大深度
        //获取x结点的右子树的最大深度
        //得到x节点的左右子树最大深度的较大值,将较大值+1 即可

        int max=0;//x的最大深度
        int maxL=0;//x的左子树的最大深度
        int maxR=0;//x的右子树的最大深度

        if(x.left!=null){
            maxL=  maxDepth(x.left);
        }
        if(x.right!=null){
            maxR= maxDepth(x.right);
        }
        max=(maxL>maxR)?maxL+1:maxR+1;

        return max;
    }

    //内部类Node 表示结点
    private class Node {
        private K key;//键
        private V value;//值
        private Node left;//左子结点
        private Node right;//右子结点

        public Node(K key,V value,Node left,Node right){
           this.key=key;
           this.value=value;
           this.left=left;
           this.right=right;
        }

    }
}

二叉树前序遍历: 先遍历根结点,再遍历左子树,最后遍历右子树
二叉树中序遍历: 先遍历左子树,再遍历根结点,最后遍历右子树
二叉树后序遍历: 先遍历左子树,再遍历右子树,最后遍历根结点

二叉树的层序遍历(广度优先): 从顶层(root)到最底层,一层一层地依次遍历,同一层中的元素从左到右遍历。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值