二叉查找树

二叉查找树(Binary Search Tree),也称有序二叉树或排序二叉树,是指一棵空树或者具有下列性质的二叉树:

  1. 若任意节点的左子树不空,则左子树上所有节点的值均小于 它的根节点的值
  2. 若任意节点的右子树不空,则右子树上所有节点的值均大于 它的根节点的值
  3. 任意节点的左右子树也分别为二叉查找树
  4. 没有键值相等的节点
    如下图,这是个普通的二叉树:
    普通二叉树

    在此基础上,加上节点之间的大小关系,就是二叉查找树:
    二叉查找树

Java实现二叉查找树的基本操作

定义

首先定义一个二叉树节点类,主要包含三个成员变量,两个指向左右节点的left,right;一个用于排序的键值data。

package cn.ihep.tree;

public class BinaryTreeNode {
    public int data;
    public BinaryTreeNode left;
    public BinaryTreeNode right;

    public BinaryTreeNode(int data, BinaryTreeNode left, BinaryTreeNode right) {
        this.data = data;
        this.left = left;
        this.right = right;
    }
}

遍历二叉查找树

根据二叉查找树的性质,我们容易知道对二叉查找树进行中序遍历可以得到一个递增序列。

    // 遍历输出二叉排序中的内容(中序输出)
    public void selectOutput(BinaryTreeNode root) {
        if (root != null) {
            this.selectOutput(root.left);
            System.out.print(root.data + " ");
            this.selectOutput(root.right);
        }
    }

插入

在一个二叉查找树中插入一个键值key,原理很容易理解,拿key和当前节点的键值对比,如果key小于当前节点的键值,就把key插到当前节点的左子树中,依次递归去寻找查找位置。

    // 插入节点
    public void addNode(BinaryTreeNode root, int data) {
        if (root == null)
            root = new BinaryTreeNode(data, null, null);
        else {
            if (root.data > data) {
                // left tree
                if (root.left == null) {
                    root.left = new BinaryTreeNode(data, null, null);
                } else {
                    root = root.left;
                    addNode(root, data);
                }
            } else if (root.data < data) {
                // right tree
                if (root.right == null) {
                    root.right = new BinaryTreeNode(data, null, null);
                } else {
                    root = root.right;
                    addNode(root, data);
                }
            } else {
                System.out.println("原二叉查找树已有该值,无法插入。。。");
            }
        }
    }

初始化一棵二叉查找树

初始化一棵二叉查找树,我们可以用在数组排列的时候,或者查找值的时候,先构建一个二叉查找树,那么这个序列就有序了,然后我们再去根据其它算法进行查找,效率可能会更高。(但我总觉得构建一个二叉查找树挺麻烦的呀)

    // 初始化二叉查找树
    public BinaryTreeNode initBinarySearchTree(int[] nums) {
        this.root = new BinaryTreeNode(nums[0], null, null);
        for (int i = 1; i < nums.length; i++) {
            addNode(root, nums[i]);
        }
        return this.root;
    }

删除

二叉查找树的删除是比较麻烦的,我们可以看下面这张图:


image.png

如果,我们要删除的节点没有子节点,直接将父节点指向该节点的link置为null即可;


当删除的节点只有1个子节点时,将该子节点替换为要删除的节点即可,如下图:(图中还显示更新节点数量了,我这里没有设置节点数量的字段,可忽略)


image.png

但是,当删除的节点有2个子节点时,问题就比较复杂了:
我们可以采取两种方法,第一,把待删除节点的左子树中的最大值替换掉删除节点;第二,把待删除节点的右子树中的最小值替换掉删除节点。(我这里将右子树中最小值作为替换点)

   /**
     * 
     * @param root
     * @param key:删除该节点
     * @return:返回删除后的二叉查找树的根节点
     */
    public BinaryTreeNode deleteNode(BinaryTreeNode root, int key) {
        BinaryTreeNode s;
        if (root.data > key)
            root.left = deleteNode(root.left, key);
        else if (root.data < key)
            root.right = deleteNode(root.right, key);
        else {
            // 找到要删除的值
            if (root.left != null && root.right != null) {
                s = root.right;
                while (s.left != null) {
                    s = s.left;
                }
                root.data = s.data;
                root.right = deleteNode(root.right, s.data);
            } else {
                root = (root.left != null) ? root.left : root.right;
            }
        }
        return root;
    }

总结:
二叉查找树的运行时间和树的形状有关,树的形状又和元素插入顺序有关。在最好的情况下,节点完全平衡,从根节点到最底层叶子节点只有lgN个节点。在最差的情况下,根节点到最底层叶子节点会有N个节点。


最后,附上整个测试源码:(BinarySearchTree的定义见本文第一段代码)

package cn.ihep.tree;

/**
 * 二叉查找树的实现 -- 查找、插入、删除
 * 
 * @author xiaoming
 *
 */
public class BinarySearchTree {
    public BinaryTreeNode root;

    // 初始化二叉查找树
    public BinaryTreeNode initBinarySearchTree(int[] nums) {
        this.root = new BinaryTreeNode(nums[0], null, null);
        for (int i = 1; i < nums.length; i++) {
            addNode(root, nums[i]);
        }
        return this.root;
    }

    // 插入节点
    public void addNode(BinaryTreeNode root, int data) {
        if (root == null)
            root = new BinaryTreeNode(data, null, null);
        else {
            if (root.data > data) {
                // left tree
                if (root.left == null) {
                    root.left = new BinaryTreeNode(data, null, null);
                } else {
                    root = root.left;
                    addNode(root, data);
                }
            } else if (root.data < data) {
                // right tree
                if (root.right == null) {
                    root.right = new BinaryTreeNode(data, null, null);
                } else {
                    root = root.right;
                    addNode(root, data);
                }
            } else {
                System.out.println("原二叉查找树已有该值,无法插入。。。");
            }
        }
    }

    // 遍历输出二叉排序中的内容(中序输出)
    public void selectOutput(BinaryTreeNode root) {
        if (root != null) {
            this.selectOutput(root.left);
            System.out.print(root.data + " ");
            this.selectOutput(root.right);
        }
    }

    /**
     * 
     * @param root
     * @param key:删除该节点
     * @return:返回删除后的二叉查找树的根节点
     */
    public BinaryTreeNode deleteNode(BinaryTreeNode root, int key) {
        BinaryTreeNode s;
        if (root.data > key)
            root.left = deleteNode(root.left, key);
        else if (root.data < key)
            root.right = deleteNode(root.right, key);
        else {
            // 找到要删除的值
            if (root.left != null && root.right != null) {
                s = root.right;
                while (s.left != null) {
                    s = s.left;
                }
                root.data = s.data;
                root.right = deleteNode(root.right, s.data);
            } else {
                root = (root.left != null) ? root.left : root.right;
            }
        }
        return root;
    }

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

推荐阅读更多精彩内容