二叉搜索树(Java实现)

  1. Java代码

/**
 * 二叉搜索树,不可以保存null.可以保存相同的元素
 * 二叉搜索树,左子树小于父节点,右子树大于父节点.
 */
public class BinarySearchTree {
    //数据数量
    private int size;
    //根节点,null表示为空树
    private Node root;

    //节点对象
    private class Node {
        //保存数据
        int elem;
        //保存相同数据数量
        int number;
        //指向左孩子
        Node lChildren;
        //指向右孩子
        Node rChildren;
        //指向父节点,在删除的时候比较容易定位。
        Node parent;


        public Node(int elem, Node lChildren, Node rChildren, Node parent) {
            this.elem = elem;
            this.lChildren = lChildren;
            this.rChildren = rChildren;
            this.parent = parent;
            number = 1;
        }
    }

    /**
     * 添加元素,不能添加null
     */
    BinarySearchTree addNode(Integer elem) {
        if (elem == null) {
            throw new UnsupportedOperationException("不能插入空元素");
        }
        Node addNode = new Node(elem, null, null, null);
        if (root == null) {
            root = addNode;
        } else {
            //遍历插入
            addNode(root, addNode);
        }
        size++;
        return this;
    }

    /**
     * 遍历添加节点
     * 终止条件:添加到合适的位置
     * 如果已经存在相同的数据则将节点的number+1
     * 根节点的数据小于添加节点的数据,则遍历根节点的右子树
     * 根节点的数据大于添加节点的数据,则遍历根节点的左子树
     * 总是添加到叶子节点
     *
     * @param parent  根节点
     * @param addNode 添加的节点
     */
    private void addNode(Node parent, Node addNode) {
        if (parent.elem < addNode.elem) {
            if (parent.rChildren != null) {
                addNode(parent.rChildren, addNode);
            } else {
                addNode.parent = parent;
                parent.rChildren = addNode;
            }
        } else if (parent.elem > addNode.elem) {
            if (parent.lChildren != null) {
                addNode(parent.lChildren, addNode);
            } else {
                addNode.parent = parent;
                parent.lChildren = addNode;
            }
        } else {
            parent.number++;
        }
    }

    int height() {
        return height(root);
    }

    /**
     * 递归求树的高度
     * 终止条件:根节点为null,返回0
     * 求右子树高度rightSize
     * 求左子树高度leftSize
     *
     * @param parent 根节点
     * @return 返回左子树高度和右子树高度的最大值。也即是树的高度
     */
    private int height(Node parent) {
        if (parent == null) {
            return 0;
        }
        int leftSize = 1 + height(parent.lChildren);
        int rightSize = 1 + height(parent.rChildren);
        return Math.max(leftSize, rightSize);
    }

    void inOrderPrint() {
        inOrderPrint(root);
    }

    /**
     * 中序遍历   左节点--根节点--有节点
     *
     * @param root
     */
    private void inOrderPrint(Node root) {
        if (root != null) {
            //递归左子树
            inOrderPrint(root.lChildren);
            System.out.println("elem:" + root.elem + ",number:" + root.number);
            //递归右子树
            inOrderPrint(root.rChildren);
        }
    }

    int size() {
        return size;
    }

    /**
     * 删除元素,若元素存在1个以上则将该节点的number - 1
     *
     * @param elem 待删除的元素
     * @return true:删除成功<br/> false:删除失败(没有查询到)
     */
    Integer del(Integer elem) {
        return del(root, elem);
    }

    /**
     * 删除节点一共分为三大类情况
     *  delNode:删除节点,delParent:delNode的父节点,root:根节点
     * 1:delNode没有子树
     *   (1):删除root,也即是delNode的parent==null。直接将root置空
     *   (2):删除非root,delParent和delNode断开连接也即是delParent.rChildren=null(or delParent.lChildren=null)
     * 2:delNode只有一个子树
     *   (1):删除root,将root指向delNode
     *   (2):删除非root,将delParent的下一个节点指向delNode的子节点
     *       也即是delParent.rChildren=delParent.rChildren(or delParent.lChildren=delParent.lChildren)
     * 3:delNode有两个子树
     *   Step One:找寻delNode左子树的最大节点MaxNode(or 找寻右子树的最小节点MinNode),交换delNode和MaxNode(or MinNode)的数据
     *   Step Two:
     *        (1):delNode左子树的maxNode为delNode.lChildren(or delNode右子树的minNode为delNode.rChildren)
     *            delNode.lChildren=maxNode.lChildren(or delNode.rChildren=minNode.rChildren)
     *        (2):delNode左子树的maxNode不是delNode.lChildren(or delNode右子树的minNode不是delNode.rChildren)
     *            delNode.parent.rChildren=maxNode.lChildren(or delNode.parent.lChildren=minNode.rChildren)
     *
     * @param parent
     * @param elem
     * @return
     */
    private Integer del(Node parent, Integer elem) {
        //搜索待删除的元素
        Node node = searchNode(parent, elem);
        if (node == null) {
            return null;
        }
        Integer result = node.elem;
        if (node.number != 1) {
            node.number--;
            return result;
        }
        Node delParent = node.parent;
        //删除节点没有孩子
        if (node.lChildren == null && node.rChildren == null) {
            //删除的为根节点
            if (delParent == null) {
                //根节点
                root = null;
            } else {
                //判断是左孩子还是右孩子
                if (delParent.lChildren == node) {
                    delParent.lChildren = null;
                } else {
                    delParent.rChildren = null;
                }
                //Help GC
                node.parent = null;
            }
        }
        //删除节点只有一个孩子
        if (node.lChildren != null && node.rChildren == null) {
            //删除的为根节点
            if (delParent == null) {
                //根节点 node == root
                root = node.lChildren;
                root.parent = null;
                //Help GC
                node.lChildren = null;
            } else {
                delParent.lChildren = node.lChildren;
                node.lChildren.parent = delParent;
                //Help GC
                node.lChildren = null;
                node.parent = null;
            }
        }
        if (node.lChildren == null && node.rChildren != null) {
            if (delParent == null) {
                //根节点
                root = node.rChildren;
                root.parent = null;
                //Help GC
                node.rChildren = null;
            } else {
                //断开连接
                delParent.rChildren = node.rChildren;
                //添加父节点
                node.rChildren.parent = delParent;
                //Help GC
                node.rChildren = null;
                node.parent = null;
            }
        }

        //删除节点有两个孩子
        if (node.rChildren != null && node.lChildren != null) {
            //可能拥有左子树
            Node maxNode = findMaxNode(node.lChildren);
            //和当前节点交换内容
            node.elem = maxNode.elem;
            node.number = maxNode.number;
            //获取删除元素的父节点
            delParent = maxNode.parent;
            //交换元素为删除元素的右节点
            if (delParent == node) {
                delParent.lChildren = maxNode.lChildren;

                if (delParent.lChildren != null) {
                    delParent.lChildren.parent = delParent;
                }
                //Help GC
                maxNode.lChildren = null;
                maxNode.parent = null;
            } else {
                delParent.rChildren = maxNode.lChildren;
                if (delParent.rChildren != null) {
                    delParent.rChildren.parent = delParent;
                }
                //Help GC
                maxNode.parent = null;
                maxNode.lChildren = null;
            }
        }


        return result;
    }

    boolean search(Integer elem) {
        return searchNode(root, elem) == null ? false : true;
    }

    private Node searchNode(Node parent, Integer elem) {
        if (parent == null) {
            return null;
        }

        if (parent.elem > elem) {
            return searchNode(parent.lChildren, elem);
        } else if (parent.elem < elem) {
            return searchNode(parent.rChildren, elem);
        } else {
            return parent;
        }
    }

    /**
     * 查找元素最大节点
     * 根据搜索二叉树的性质可知右子树所有节点的数据大于左子树的所有节点
     * 因此只要找到最左的元素即可
     *
     * @param parent 根节点
     * @return null:没找到<br/>otherwise:找到最大元素节点
     */
    private Node findMaxNode(Node parent) {
        if (parent == null) {
            return null;
        }
        while (parent.rChildren != null) {
            parent = parent.rChildren;
        }
        return parent;
    }

    /**
     * 查找元素最小节点
     * 根据搜索二叉树的性质可知左子树所有节点的数据小于右子树的所有节点
     * 因此只要找到最右的元素即可
     *
     * @param parent 根节点
     * @return null:没找到<br/>otherwise:找到最小元素节点
     */
    private Node findMinNode(Node parent) {
        if (parent == null) {
            return null;
        }
        while (parent.lChildren != null) {
            parent = parent.lChildren;
        }
        return parent;
    }
  1. 测试

 @Test
    public void testBinarySearchTree() {
        BinarySearchTree binarySearchTree = new BinarySearchTree();
        /**
         * 构造树:
         *            1
         *       -1       5
         *          0   4    6
         *                     8
         *                   7
         */
        binarySearchTree.addNode(1)
                .addNode(5)
                .addNode(6)
                .addNode(8)
                .addNode(5)
                .addNode(7)
                .addNode(-1)
                .addNode(0)
                .addNode(4);
        System.out.println("binarySearchTree height:" + binarySearchTree.height());
        binarySearchTree.inOrderPrint();
        System.out.println(binarySearchTree.search(9));
        System.out.println(binarySearchTree.search(4));
        System.out.println(binarySearchTree.search(5));
        System.out.println(binarySearchTree.search(6));
        System.out.println(binarySearchTree.size());

        System.out.println(binarySearchTree.del(7));

        System.out.println(binarySearchTree.del(9));
        /**
         * 删除后:
         *            1
         *       -1       5
         *          0   4    6
         *                     8
         */
        binarySearchTree.inOrderPrint();

        System.out.println(binarySearchTree.del(6));
        /**
         * 删除后:
         *            1
         *       -1       5
         *          0   4    8
         */
        binarySearchTree.inOrderPrint();

        System.out.println(binarySearchTree.del(5));
        System.out.println(binarySearchTree.del(5));
        /**
         * 删除后:
         *            1
         *       -1       8
         *          0   4
         */
        binarySearchTree.inOrderPrint();


        System.out.println(binarySearchTree.del(1));
        /**
         * 删除后:
         *            8
         *       -1       4
         *          0
         */
        binarySearchTree.inOrderPrint();

        System.out.println(binarySearchTree.del(4));
        /**
         * 删除后:
         *            8
         *       -1
         *          0
         */
        binarySearchTree.inOrderPrint();

        System.out.println(binarySearchTree.del(-1));
        /**
         * 删除后:
         *            8
         *        0
         */
        binarySearchTree.inOrderPrint();
        System.out.println(binarySearchTree.del(8));
        /**
         * 删除后:
         *        0
         */
        binarySearchTree.inOrderPrint();
        System.out.println(binarySearchTree.del(0));
        /**
         * 删除后:
         *        null
         */
        binarySearchTree.inOrderPrint();
    }

测试结果

binarySearchTree height:5
elem:-1,number:1
elem:0,number:1
elem:1,number:1
elem:4,number:1
elem:5,number:2
elem:6,number:1
elem:7,number:1
elem:8,number:1
false
true
true
true
9
7
null
elem:-1,number:1
elem:0,number:1
elem:1,number:1
elem:4,number:1
elem:5,number:2
elem:6,number:1
elem:8,number:1
6
elem:-1,number:1
elem:0,number:1
elem:1,number:1
elem:4,number:1
elem:5,number:2
elem:8,number:1
5
5
elem:-1,number:1
elem:0,number:1
elem:1,number:1
elem:4,number:1
elem:8,number:1
1
elem:-1,number:1
elem:0,number:1
elem:4,number:1
elem:8,number:1
4
elem:-1,number:1
elem:0,number:1
elem:8,number:1
-1
elem:0,number:1
elem:8,number:1
8
elem:0,number:1
0

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,014评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,796评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,484评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,830评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,946评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,114评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,182评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,927评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,369评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,678评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,832评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,533评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,166评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,885评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,128评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,659评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,738评论 2 351

推荐阅读更多精彩内容