二叉搜索树的实现——专注于代码的实现,代码里有详细的注释

二叉搜索树(BST-binary search tree)

1.基本概述

  • 顾名思义:二叉搜索树就是一颗二叉树。具有二叉树的性质

  • 特性:左子节点的值<=根节点的值<=右子节点的值

假设x为BST里的一个节点:

  • 如果x.left为x的左孩子里的一个节点,那么 x . l e f t . k e y < = x . k e y x.left.key<=x.key x.left.key<=x.key
  • 如果x.right为x的右孩子里的一个节点,那么 x . r i g h t . k e y > = x . k e y x.right.key>=x.key x.right.key>=x.key
  • 满足: x . l e f t . k e y < = x . k e y < = x . r i g h t . k e y x.left.key <= x.key<=x.right.key x.left.key<=x.key<=x.right.key
    如图为算法导论中的描述。注意在我们用代码实现的时候,不考虑值相等的情况(较为复杂)请添加图片描述

2.迭代代码的实现

删除操作在代码里有详细的注释


import java.util.Stack;

/**
 * @D:以迭代形式实现一个二叉搜索树
 */

public class BST_Iterative{
    Node root;
    public BST_Iterative(){
        this.root = null;
    }


    public static void main(String[] args) {
        BST_Iterative bs = new BST_Iterative();
        bs.insert(7);
        bs.insert(8);
        bs.insert(5);
        bs.insert(3);
        bs.insert(2);
        bs.insert(1);
        bs.inorder();
        bs.remove(3);
        bs.inorder();
    }


    /**
     * @D:向二叉搜索树中插入节点,如果已经存在,直接返回——在树中不允许插入重复的数据
     * @param data
     */
    public void insert(int data){
        Node parent = null;
        Node temp = this.root;//在这里我们用一个临时节点代替root节点
        int rightOrLeft = -1;//-1代表根节点,0代表左节点,1代表右节点

        //寻找一个正确的位置来及逆行插入
        while (temp != null){
            if (temp.data>data) {   //进入左节点
                parent = temp;
                temp = parent.left;
                rightOrLeft = 0;
            }else if(temp.data<data){
                parent = temp;
                temp = parent.right;
                rightOrLeft = 1;
            }else{
                System.out.println(data+"已经存在");
                return;
            }
        }

        //开始进行插入操作
        Node newNode = new Node(data);

        //try:parent变为rightOrLeft=0;parent.left变为temp;
        //this.root是一个全局变量,进行操作的时候尽量用parent这个节点。此时我们始终都没有修改root。不然会将其修改
        //这样,建完一棵树,全局变量root就是根节点

        if (parent==null) {     //parent如果还是null,代表此时根节点是null,将数值插入根节点
            this.root = newNode;
        }else{
            if (rightOrLeft==0) {   //左子节点符合条件
                parent.left = newNode;
            }else{
                parent.right = newNode;
            }
        }
    }


    /**
     * @D:返回一个值是否存在
     * @param data
     * @return
     */
    public boolean find(int data){
        Node temp = this.root;

        while (temp != null) {
            if (temp.data>data) {
                temp = temp.left;
            }else if (temp.data<data){
                temp = temp.right;
            }else{
                System.out.println(data+"存在");
                return true;
            }
        }
        System.out.println(data+"没有找到");
        return false;
    }


    /**
     * @D:删除BST中的某个节点
     * @param data
     */
    public void remove(int data){
        Node parent = null;//要删除节点的父节点
        Node temp = this.root;//临时节点存储节点自己(要删除的节点)
        int rightOrLeft = -1;//记录左右子节点


        //找到要删除的节点
        while (temp != null) {
            if (temp.data ==data) {
                break;
            }else if(temp.data>data){
                parent = temp;
                temp = parent.left;
                rightOrLeft = 0;
            }else{
                parent = temp;
                temp = parent.right;
                rightOrLeft = 1;
            }
        }


        //开始执行删除
        /**
         * 1. 待删除如果是叶子节点,那么就直接删除
         * 2. 待删除节点的左节点或者右节点为空,那么就直接删除,左节点或者右节点上浮(左子树或者右子树上浮)
         * 3. 待删除节点的左右孩子(左右子树)都存在——寻找其左子树的最大值所在的节点(寻找其右子树的最小值所在的节点)将这个节点删除并放置到待删除的节点处以保持BST的性质
         *      1.待删除节点的右孩子没有左孩子,那么待删除节点的右孩子就是最小值
         *      2.沿着左子树下沉,寻找最小节点
         *
         * 注意:在这里所使用的方法是先用一个replacement节点保存信息——用来代替待删除的节点
         */
        //如果temp为空,那么给定的值并不在BST中
        if (temp!=null) {
            Node replacement;   //为被删除的节点存储新的值,同时如果我们上浮某些值的会删除上浮的值
            if (temp.right == null && temp.left==null) {    //叶子节点
                replacement = null;
            } else if (temp.right == null) {        //右子节点为空,那么左节点直接上浮。注意:这里只是临时存储
                replacement = temp.left;            //replacement指向temp.left;(注意:temp.left是待删除结点的左子节点,其后面可能还有许多左子节点,这里更改地址)
                temp.left = null;
            }else if(temp.left == null){
                replacement = temp.right;
                temp.right = null;
            }else{                          //左右节点都存在。寻找待删除节点右子树中的最小值(或者左子树中的最大值)
                if (temp.right.left==null) {        //如果待删除节点的右节点的左节点不存在。那么待删除节点的右节点就是左子树最右边的节点(待删除节点的右子节点就是其右子树的最小值)
                    temp.data = temp.right.data;
                    replacement = temp;
                    temp.right = temp.right.right;//删除temp.right
                }else{                      //继续寻找待删除节点右子树的最小值
                    Node parent2 = temp.right;
                    Node child = temp.right.left;       //左节点肯定是最小的。因此不断遍历子节点
                    while (child.left != null) {        //直到左节点为空
                        parent2 = child;
                        child = parent2.left;
                    }
                    temp.data = child.data;
                    parent2.left = child.right;     //删除左节点,再连接其右子树。(注意,此时parent右子树最小左节点的父节点,child是右子树最小左节点,其右子树肯定小于parent(child再parent的左子树中))
                    replacement = temp;
                }
            }


            //在执行删除后,更改其父节点的资料
            if (parent==null) {
                this.root = replacement;//直接代替根节点
            }else{
                if (rightOrLeft==0) {
                    parent.left = replacement;
                }else{
                    parent.right = replacement;
                }
            }


        }
    }

    /**
     * 中序遍历,根据二叉搜索树的特性,这里可以按顺序输出
     */
    public void inorder() {
        if (this.root == null) {
            System.out.println("This BST is empty.");
            return;
        }
        System.out.println("Inorder traversal of this tree is:");
        Stack<Node> st = new Stack<Node>();
        Node cur = this.root;
        while (cur != null || !st.empty()) {
            while (cur != null) {
                st.push(cur);
                cur = cur.left;
            }
            cur = st.pop();
            System.out.print(cur.data + " ");
            cur = cur.right;
        }
        System.out.println(); // for next line
    }

    private class Node{
        int data;
        Node left;
        Node right;

        /**
         * 构造器
         */
        Node(int d) {
            data = d;
            left = null;
            right = null;
        }
    }

}

3.递归的形式

/**
 * 以递归的形式实现一个二叉搜索树
 */




public class BST_Recursive {

    Node root;
    public BST_Recursive(){
        this.root = null;
    }

    public static void main(String[] args) {
        BST_Recursive bs = new BST_Recursive();
        bs.add(14);
        bs.add(9);
        bs.add(16);
        bs.add(5);
        bs.add(10);
        bs.add(15);
        bs.add(18);
        bs.add(1);
        bs.add(6);bs.add(17);bs.add(20);
        bs.inorder();
        bs.remove(16);

    }


    /**
     * @D:用递归方法删除某个节点
     * @param node  当前节点,用来寻找数据(一般从根节点开始寻找)
     * @param data  要删除的数据
     * @return Node     这个node是根节点,注意在我们运行完程序后,程序是要回溯到最初的那个节点的
     */
    private Node delete(Node node,int data){
        if (node==null) {           //跳出循环
            System.out.println("BST里没有这个数据了");
        }else if(node.data >data){  //w往左子树进行寻找
            node.left = delete(node.left,data);
        }else if(node.data<data){   //往右子树进行寻找
            node.right = delete(node.right,data);
        }else{
            if (node.right==null && node.left==null) {      //叶子节点,直接标记为null
                node = null;
            }else if(node.left ==null){         //待删除节点左子树为空,右子树上浮
                Node temp = node.right;         //保存右子树的地址信息
                node.right = null;              //删除
                node = temp;                    //更新
            }else if(node.right == null){
                Node temp = node.left;
                node.left = null;
                node = temp;
            }else{
                Node temp = node.right;
                while (temp.left != null) {     //寻找右子树中的最小值
                    temp = temp.left;
                }
                node.data = temp.data;          //待删除节点的值更新
                node.right = delete(node.right, node.data);     //这是什么意思呢?首先确定一点就是node的右子树的所有值肯定要大于node.data的。那么这行代码的意思应该就是要开始回溯了
            }
        }
        return node;
    }
    public void remove(int data){
        this.root = delete(this.root,data);
    }


    /**
     * @D:递归的插入数据
     * @param node      一般从根节点开始向下搜寻
     * @param data
     * @return node 根节点,在运行完程序之后是要回溯到最初的那个节点
     */
    private Node insert(Node node,int data){
        if (node==null) {               //结束天健
            node = new Node(data);
        }else if (node.data > data) {
            node.left = insert(node.left,data);
        }else if(node.data < data){
            node.right = insert(node.right,data);
        }
        return node;
    }
    //对insert方法的调用
    public void add(int data){
        this.root = insert(this.root,data);
    }


    /**
     * 寻找某个值
     * @param node
     * @param data
     * @return
     */
    private boolean search(Node node, int data) {
        if (node == null) {
            return false;
        } else if (node.data == data) {
            return true;
        } else if (node.data > data) {
            return search(node.left, data);
        } else {
            return search(node.right, data);
        }
    }
    //对search的实现
    public boolean find(int data) {
        if (search(this.root, data)) {
            System.out.println(data + " 在BST树中");
            return true;
        }
        System.out.println(data + " 没有找到.");
        return false;
    }



    /*------------------------------------------------------遍历算法----------------------------------------------------*/
    private void preOrder(Node node) {
        if (node == null) {
            return;
        }
        System.out.print(node.data + " ");
        if (node.left != null) {
            preOrder(node.left);
        }
        if (node.right != null) {
            preOrder(node.right);
        }
    }

    private void postOrder(Node node) {
        if (node == null) {
            return;
        }
        if (node.left != null) {
            postOrder(node.left);
        }
        if (node.right != null) {
            postOrder(node.right);
        }
        System.out.print(node.data + " ");
    }

    private void inOrder(Node node) {
        if (node == null) {
            return;
        }
        if (node.left != null) {
            inOrder(node.left);
        }
        System.out.print(node.data + " ");
        if (node.right != null) {
            inOrder(node.right);
        }
    }

    public void inorder() {
        System.out.println("Inorder traversal of this tree is:");
        inOrder(this.root);
        System.out.println(); // for next line
    }

    public void postorder() {
        System.out.println("Postorder traversal of this tree is:");
        postOrder(this.root);
        System.out.println(); // for next li
    }

    public void preorder() {
        System.out.println("Preorder traversal of this tree is:");
        preOrder(this.root);
        System.out.println(); // for next li
    }




    private class Node{
        int data;
        Node left;
        Node right;

        public Node(int data) {
            this.data = data;
            this.left = null;
            this.right = null;
        }
    }
    
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值