二叉树原理及实现

完整代码在这里 https://github.com/zhangjunapk/half_search_tree

 

树被广泛使用,比如文件系统,unix上用到了红黑树,windows上用到了树

二分查找树可以说是一个有序的集合,节点之间用链表链接起来,可以用二分搜索的方式来对搜索

二分查找树在写入上做了一个性能的权衡,每次写入数据都要遍历,然后放到合适的位置

是一种很平衡的数据存储结构

先看看百度百科对二分搜索树的定义把

 

只要满足这些条件的树都能被称为二分查找树

比如可以是这样子

   这两个都能被称为二分查找树

 

接下来我们看看为什么二分查找树为什么快

如果我们仅仅用链表的话,需要遍历所有节点,直到找到值

加入我们查找6

传统链表就需要顺序遍历节点,直到找到对应的值

 

但是二分搜索树就不同了,在插入的时候就是排序好的,查找的话直接搜索就行了

在搜索的时候,会判断当前数值是否一致,然后把要搜索的数值和当前节点进行大小对比,如果大于当前,那就从左边继续递归遍历(左边没有就结束),右边也是一样,这样就实现了二分查找(二分查找有序数组也有和当前节点对比的逻辑,然后切换索引),二分查找树比数组方便的地方就是不用再进行一次排序了,因为插入的时候就保证了顺序性。当然这是有代价的,那就是插入的时候没有数组性能高

接下来看插入代码的逻辑

 

 public void insert(String k,Integer v){
        //先判断root是否为空
        if(root==null){
            root=new Node(k,v);
            System.out.println("直接插到根 ");
            return;
        }

        //接下来遍历节点
        Node node = new Node(k, v);
        forInsert(node,root);
        System.out.println("--------------------------------");
    }

先看根节点是否为空,如果为空就直接初始化根节点,不然执行递归插入

 

 //递归遍历,然后插入数值
    private int forInsert(Node insertNode, Node node) {
        //百度百科上说键值不能相等
        if(insertNode.getVal().equals(node.getVal()))
            return -1;


        //判断都空的
        if(node.getLeft()==null&&node.getRight()==null){
            System.out.println("两边都空");
            //判断这个数值和当前节点的大小
            if(insertNode.getVal()<=node.getVal()){
                System.out.println("    插入到"+node.getVal()+"的左边"+insertNode.getVal());
                node.setLeft(insertNode);
                return 0;
            }
            if(insertNode.getVal()>=node.getVal()){
                System.out.println("    插入到右边");
                node.setRight(insertNode);
                return 0;
            }
        }

        //判断要插入的值是否比当前的节点大
        if(insertNode.getVal()<=node.getVal()){
            if(node.getLeft()!=null){
                return forInsert(insertNode,node.getLeft());
            }
            //不然直接insert
            node.setLeft(insertNode);
            return 0;
        }
        if(insertNode.getVal()>=node.getVal()){
            if(node.getRight()!=null){
                return forInsert(insertNode,node.getRight());
            }
            //不然直接insert
            node.setRight(insertNode);
            return 0;
        }
        return -1;
    }

先判断左右是否都为空,为空就把要插入的数值和当前的节点进行对比,然后直接set进去

然后就递归插入,进入下一层,然后接着判断,直到左右都为空,就接着判断大小,然后set进去

 

数据插入进去了,我们就需要拿出来,这里我写了三种遍历方式

1.先序遍历(深度优先)

2.后序遍历(深度优先)

3.按层遍历(广度优先)

先看广度优先的

 //按层遍历
    public void eachLayer(){
        System.out.println(root.getVal());
        layEach(root);
    }

这里先输出根节点

然后调用递归遍历层的方法

 

//广度优先(按层遍历里面的递归)
    private void layEach(Node node) {
        if(node.getLeft()!=null){
            System.out.println("L  "+node.getLeft().getVal());
        }
        if(node.getRight()!=null){
            System.out.println("R  "+node.getRight().getVal());
        }

        if(node.getLeft()!=null){
            layEach(node.getLeft());
        }
        if(node.getRight()!=null){
            layEach(node.getRight());
        }

    }

这里直接判断是否为空,然后直接打印

这里我这样写是有原因的,因为需要一层一层打印,所有就先输出,然后进入下一层递归

 

这就是调用层级遍历的输出

 

然后是先序遍历

 

  //递归先序遍历
    private void eachBefore(Node node) {
        System.out.println(node.getVal());

        if(node.getLeft()!=null)
            eachBefore(node.getLeft());

        if(node.getRight()!=null)
            eachBefore(node.getRight());
    }

 

先序遍历是从根节点开始遍历,然后依次遍历到左子树的叶,然后才会遍历右子树(层级调用真好玩)

接下来是后序遍历

 //递归后序遍历
    private void eachAfter(Node node) {
        System.out.println(node.getVal());

        if(node.getRight()!=null)
            eachBefore(node.getRight());

        if(node.getLeft()!=null)
            eachBefore(node.getLeft());

    }

 

这里把先序遍历换一下就行了,先遍历右子树,再遍历左子树

 

这是完整代码  https://github.com/zhangjunapk/half_search_tree

 

 

 

 

 

 

 

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值