如何写一棵简单的二叉查找树

二叉查找树

完整代码:
https://github.com/problemin/Algorithm/blob/master/src/Tree/BSTree.java

二叉排序树(Binary Sort Tree)又称二叉查找树(Binary Search Tree),亦称二叉搜索树。

  1. 若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
  2. 若右子树不空,则右子树上所有结点的值均大于它的根结点的值
  3. 左、右子树也分别为二叉排序树;

这样的树有一个特点:所有节点在x轴的映射是有序的。
这里写图片描述

一棵查找树的主要功能有:插入,查找,删除。
除此之外还需要一些辅助功能,如节点统计等。

闲话少说,我们从头来缕。

先定义一棵树,包含了基本结构

public class BSTree{
    /*
    *根节点以及节点的定义
    *此处我们先简单定义一个可以用的就行,因为太复杂容易造成思维混乱
    */
    private Node root;
    class Node{
        int key;//用来寻找位置的依据,索引
        Node left,right;
        public Node(int key){
            this.key = key;
        }
    }

    public void add(int key){}//添加节点
    public void get(int key){}//查询节点
    public void remove(int key){}//删除节点
}

ok,现在让我们挨个把功能实现了。

添加节点

迭代写法步骤为:
1.先看有没有根节点,如果没有那我们就要先造一个根节点了。
这里写图片描述

2.有了根节点,我们接下来的插入就要进行二分了,新建一个current,让他等于root。
其实就是从root开始,如果插入值比当前节点小,那么current=current.left;
如果比当前节点大,那么current=current.right;
如果相等了,就覆盖掉。
如果当前节点为null,因为找到这了,四下无人,那就说明这就是新节点该待的位置
这里写图片描述

迭代写法

public void add(int key){
    if(root == null){
            root = new Node(key);
    }else{
        Node current = root;
        while(current!=null){
            if(key<current.key){
                if(current.left!=null){
                    current = current.left;
                }else{
                    current.left = new Node(key);
                    return;
                }
            }else if(key>current.key){
                if(current.right!=null){
                    current = current.right;
                }else{
                    current.right = new Node(key);
                    return;
                }
            }else{
            //此处是相等的情况,由于我们的树现在只有键,没有值,所以没有必要处理
            System.out.println("重复覆盖");
            }
            }
    }
}

递归写法

这里写图片描述

    public void add(int key){
        root = add(root,key);
    }

    public Node add(Node current,int key){
        if(current == null) return new Node(key);
        if(key<current.key){
            current.left = add(current.left,key);
        }else if(key>current.key){
            current.right = add(current.right,key);
        }else{
            //此处是相等的情况,由于我们的树现在只有键,没有值,所以没有必要处理
            System.out.println("重复覆盖");
        }
         return current;
    }

现在我们就可以插入节点了,赶紧写一个查询看看能不能查出来

查找节点

查找的话思路跟插入差不多了,就是不断的二分,如果相等了就返回,如果遍历完了没有返回,说明没找到,那么返回空
这里写图片描述

public Node search(int key){
    Node current = root;
    while(current!=null){
        if(key<current.key){
            current = current.left;
        }else if(key>current.key){
            current = current.right;
        }else{
            System.out.println("找到节点:"+current.key);
            return current;
        }
    }
    System.out.println("找不到该节点");
    return null;
}

删除节点

删除节点就有一点复杂了,因为删除完一个节点后,如何处理其子节点是个问题。
删除节点时共有如图中3,4,10节点三种情况。

  1. 需要删除的节点下并没有其他子节点。
  2. 需要删除的节点下有一个子节点(左或右)。
  3. 需要删除的节点下有两个子节点(既左右节点都存在)。
    这里写图片描述

对于节点4,很简单,节点6.left = null即可。
对于节点10,有一个子节点14,则8.right=14即可。

复杂之处在于节点3。对于3这种节点(有两个子节点),我们要先在其右子树中找到最小的一个节点,然后用这个节点替换他。
因为3节点右子树中的最小节点比3节点大,即比3节点左子树所有节点大,又比3节点右子树其他节点小,所以替换之后就可以满足二叉树的性质。
这里写图片描述

那么问题来了:如何找到要删除节点的右子树中的最小节点?

如何找到子树中的最小节点

通过二叉搜索树的性质,我们可以知道,最小的节点一定在最树的最左侧。
这里写图片描述
如果一个节点没有左子树,那么该节点为以该节点为根的子树中最小的绩点。

public Node min(Node node){
    if(node.left == null) return node;
    else return min(node.left);
}

如何删除最小节点

好了现在可以找到最小节点了,那再替换之后我们还需要在原来位置删除该最小节点。
思路就是找到最左侧的节点,用其右子树的根节点替换他。

public Node deleteMin(Node node){
    if(node.left == null) return node.right;
    node.left = deleteMin(node.left);
    return node;
}

现在开始正式删除,由于删除需要处理父子节点之间的关系,用递归会别叫好写,所以此处用递归来实现了。

    public void remove(int key){
         root = remove(root,key);
    }


    public Node remove(Node current,int key){
    //如果Current==null,说明找到最后都没有找到要删除的节点
        if(current == null) {
            System.out.println("没有要删除的节点");
            return null;
        }

        if(key<current.key){
        //如果要删除的键比当前键小,那么去左子树里做删除操作,并把操作完的左子树根节点返回
            current.left = remove(current.left,key);
        }else if(key>current.key){
        //如果要删除的键比当前键大,那么去右子树里做删除操作,并把操作完的右左子树根节点返回
            current.right = remove(current.right,key);
        }else {
            //找到要删除的键,进行删除操作
            //1.如果左子树为空,那么把右子树根节点返回作为新的根节点
            if(current.left == null) return current.right;
            //2.如果右子树为空,那么把左子树根节点返回作为新的根节点
            if(current.right == null) return current.left;
            //3.如果有两个子节点
            //保存要删除的当前节点
            Node temp = current; 
            //用右子树最小节点替换当前节点
            current = min(current.right);
            //将旧节点的左节点接到新节点上
            current.left = temp.left;
            //由于右子树中的最小节点已经被替换过来了,所以要删除,删除后我们把新的右子树根节点返回过来接到新节点上
            current.right = deleteMin(temp.right);
            System.out.println("删除节点:"+key);
        }
        return current;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值