二分搜索树

原创作品,转载请标明来源。

一、 什么是二叉树?

editByWpp.png

二叉树每个节点最多有两个孩子
二叉树每个节点最多有一个父亲

性质
  • 二叉树具有天然的递归结构
  • 每个节点的左子树也是二叉树
  • 每个节点的柚子树也是二叉树
  • 二叉树不一定是“满”的

二、什么是二分搜索树?

editByWpp.png
  • 存储的元素必须有可比较性(比如节点存储的是学生,我们可以以年龄比较或学号比较等)

注意:本文的二分搜索树不包含重复元素
如果想包含重复元素的话,只需要定义:左子树小于等于节点;或者柚子树大于节点即可

二分搜索树的增删茶

  • 新增节点

二分搜索树添加元素的非递归写法,和链表很像
本文在二分搜索树方面的实现,关注递归实现

public class BST<E extends Comparable<E>> {

private class Node{
   public   E e;
   public Noe left, right;
   public Node(E e) {
      this.e = e;
      left = null;
      right = null;
}
// 向二分搜索树中添加新的元素e -- 优化前
    public void add1(E e) {
        if (root == null) {
            root = new Node(e);
            size++;
        } else {
            add(root,e);
        }
    }

    // 向以node为根的二分搜索树中插入元素E,递归算法---优化前的插入元素部分
    private void add1(Node node, E e) {
        // 递归终止条件
       if (e.equals(node.e)) {
            return;
       } else if (e.compareTo(node.e) < 0 && node.left == null) {
           node.left = new Node(e);
           size++;
           return;
       } else if (e.compareTo(node.e) > 0 && node.right == null) {
           node.right = new Node(e);
           size++;
           return;
       }
       if (e.compareTo(node.e) < 0 && node.left != null) {
           add(node.left,e);
       } else{ // (e.compareTo(node.e) > 0 && node.right != null)
           add(node.right,e);
       }
    }
    // 向二分搜索树中添加新的元素e -- 优化后
    public void add(E e) {
       root = add(root,e);
    }
    // 向以node为根的二分搜索树中插入元素E,递归算法---优化后的插入元素部分
    // null 也是一个二叉树来考虑
    private Node add(Node node, E e) {
        // 递归终止条件
        if (node == null) {
            size++;
            return new Node(e);
        }
       if (e.compareTo(node.e) < 0) {
          node.left = add(node.left,e);
       } else if (e.compareTo(node.e) > 0) {
           node.right = add(node.right,e);
       }
       return node;
    }
}
  • 二分搜索树的查询操作
// 递归算法
    private boolean contains(Node node, E e) {
        if (node == null)
            return false;
        if (e.compareTo(node.e) == 0)
            return true;
        else if (e.compareTo(node.e) < 0)
            return contains(node.left,e);
        else //e.compareTo(node.e) > 0
            return contains(node.right,e);
    }
  • 遍历(前序、中序、后序-也叫深度优先遍历)


    editByWpp.png

    editByWpp.png
editByWpp.png
前序遍历
 public void preOrder() {
        System.out.println();
        preOrder(root);
    }
   private void preOrder(Node node) {
        if (node == null)
            return;
        System.out.println(node.e);
        preOrder(node.left);
        preOrder(node.right);
    }

    // 非递归的前序遍历
    public void preOrderNR (){

        Stack<Node> stack = new Stack();
        stack.push(root);
        while (!stack.isEmpty()) {
            Node cur = stack.pop();
            System.out.println(cur.e);
            if (cur.right != null) stack.push(cur.right);
            if (cur.left != null)  stack.push(cur.left);
        }
    }
中序遍历
    /**
     * 中序遍历
      */
    public void inOrder(){
        System.out.println();
        inOrder(root);
    }

    private void inOrder(Node node) {
        if (node == null)
            return;
        inOrder(node.left);
        System.out.print(node.e+ "  ");
        inOrder(node.right);
    }
    /**
     * 后序遍历
     */
    public void postOrder() {
        System.out.println();
        postOrder(root);
    }

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

  • 层序遍历(广度优先遍历)--更快的找到问题的解(常用于算法设计中-最短路径)


    editByWpp.png
   public void levelOrder (){
        LinkedListQueue <Node> queue = new LinkedListQueue();
        queue.enqueue(root);
        while (!queue.isEmpty()) {
            Node cur = queue.dequeue();
            System.out.println(cur.e);
            if (cur.left != null)   queue.enqueue(cur.left);
            if (cur.right != null)  queue.enqueue(cur.right);

        }
    }
  • 删除操作
    删除二分搜索树的某一个节点可能不是很好实现,我们可以先实现删除最小节点和最大节点。可以发现最小节点就是二分搜索树最左边的节点,最大节点是二分搜索书最右边的节点;代码如下
// 得到最小节点值
    public E miniMum(){
        if (size == 0)
            throw new IllegalArgumentException("BST is empty");
       return miniMum(root).e;
    }
    private Node miniMum(Node node) {
        if (node.left == null)
            return node;
        return miniMum(node.left);
    }
// 得到醉倒节点值
    public E maxMum(){
        if (size == 0)
            throw new IllegalArgumentException("BST is empty");
        return maxMum(root).e;
    }
    private Node maxMum(Node node) {
        if (node.right == null)
            return node;
        return maxMum(node.right);
    }
 public E removeMin(){
        E  ret = miniMum();
        root = removeMin(root);
        return ret;
    }
    // 删除掉以node为根的二分搜索树中的最小节点
    // 返回删除节点后新的二分搜索树的根
    private Node removeMin(Node node) {
        if (node.left == null) {
            Node rightNode = node.right;
            node.right = null;
            size--;
            return rightNode;
        }
        node.left = removeMin(node.left);
        return node;
    }
   public E removeMax(){
        E  ret = maxMum();
        root = removeMax(root);
        return ret;
    }
    // 删除掉以node为根的二分搜索树中的最大节点
    // 返回删除节点后新的二分搜索树的根
    private Node removeMax(Node node) {
        if (node.right == null) {
            Node leftNode = node.left;
            node.left = null;
            size--;
            return leftNode;
        }
        node.right = removeMax(node.right);
        return node;
    }

当我们试图删除某一个节点时,如果这个节点只有左节点,没有右节点,我们可以以删除最大节点的方式去处理(即使该节点未必是最大节点),如果只有右节点没有左节点时,我们同样可以将它以删除最小节点的方式去处理,问题是如果这个要删除的节点既有左节点又有右节点时,怎么处理呢?
我引入一句话,给出了方案

1962年,Hibgard提出- Hibgard Deletion
其实也很简单,就是找到待删除节点的后置最小节点替代待删节点;

比如下图中,d节点是待删节点,根据二分搜索树的性质,d节点的右子叶都比左子叶要打,那么我们只要找到右子叶的最小值替代当前待删节点就可以了。


editByWpp.png

另一种方案:也可以找到待删节点的前驱最大节点替代待删节点。如上图其实也可以用53所在的节点替换d节点;

写在后面

好了,现在二分搜索树的分析暂时可以告一段落了,

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

推荐阅读更多精彩内容

  • 如何查找 我们先从二分查找法开始说起,生活中,如果我们摆放物品是按照一定规律的话,那么查找起来就会非常快,如果我们...
    李威威阅读 681评论 0 2
  • 插入操作: 与根节点比较相等则覆盖其值,若小于则与左节点比较,若大与则与右节点比较,若根节点为空则插入这个位置即可...
    一个人的飘阅读 246评论 0 0
  • 二叉树 定义:二叉树是最多只有两个节点的树,二叉树具有唯一根节点。 二叉树的特点:1、二叉树每个节点最多有两个孩子...
    habit_learning阅读 1,022评论 0 0
  • 父亲一生虽未受过更高的教育,但在他的言谈和处事之中,可以看到对读书人的崇拜。村里有位年长些的老人,私塾念得很好,经...
    付志斌阅读 1,971评论 0 3
  • 1. 换机票/登机牌及行李托运 至少提前3小时(大概10:50到)到达首都国际机场2号航站楼。 下了出租车或地铁后...
    雪中飘影阅读 416评论 0 0