《算法》3.3平衡查找树

平衡查找树

1、2-3查找树

定义:

一棵2-3查找树或为一棵空树。或又以下结点组成:

  • 2-结点:含有一个键和两条连接
  • 3-结点:含有两个键和三条连接

1.1查找

和标准的二叉查找树类似

1.2插入

有以下几种情况:

1、向2-结点中插入新键

  • 把这个2-结点替换为一个3-结点

2、向一棵只含有一个3-结点的树中插入新键

  • 先将新键存入该结点,成为一个4-结点**(新建4-结点)**
  • 转换为3个2-结点**(分解)**

3、向一个父结点为2-结点的3-结点插入新键

  • 新建4-结点
  • 分解:将中键插入到父结点中

4、向一个父结点为3-结点的3-结点中插入新键

  • 新建4-结点
  • 分解:将中键插入到父结点中
  • 此时父结点变为一个4-结点
  • 递归对父结点进行同样的操作。

1.3局部变换

插入算法的变换是局部的:

  • 除了相关的结点和链接之外不会修改或者检查树的其他部分

1.4全局性质

局部变换不会影响树的全局的有序性平衡性

  • 任意空链接到根结点的路径长度都是相等的。

标准的二叉查找树是由上向下生长的,2-3树是由下向上生长的。

在一棵大小为N的2-3树中,查找和插入的操作访问的结点必然不超过lgN个。

2、红黑二叉查找树

2-3查找树性能很好,但是转换调整操作需要移动对象,太过复杂麻烦。

**红黑二叉查找树(红黑树)**就是用标准的二叉查找树和一些额外的信息来表示2-3树。

  • 红链接:将两个2-结点连接起来构成一个3-结点。
  • 黑链接:就是普通的链接。

2.1定义

红黑树就是满足以下条件的二叉查找树:

  • 红链接均为左链接
  • 没有任何一个结点同时和两条红链接相连
  • 是完美黑色平的,即任意空链接到根结点的路径上的黑链接数量都是相等的。

我们将指向结点自己的链接的颜色存储在自己上。

2.2旋转

旋转操作可以改变红链接的指向。

左旋

目的:将一条红色右链接转化为一条红色左链接。

操作:

  • 红色右链接连接着一个子结点和一个父结点,
  • 只需要将子结点变为父结点,父结点变为左子结点。

右旋

和左旋相反。

2.3插入

有以下几种情况:

1、向单个2-结点插入新键

  • 如果新键小于老键:直接在左新增一个红色结点
  • 如果新键大于老键:在右新增一个红色结点,再左旋。

2、向树底的2-结点插入新键

  • 同上。

3、向一棵双键树(即一个3-结点)插入新键

分3种情况:

  • 如果新键大于原树的两个键:在父结点的右新建一个红色结点,将两条红链接变成黑色。
  • 如果新键小于原树的两个键:在子结点的左新建一个红色结点,将上层的红链接右旋,得到第一种情况。
  • 如果新键介于原树的两个键之间:在子结点的右新建一个红色结点,将下层的红链接左旋,得到第二种情况。

4、向树底的3-结点插入新键

  • 同上

2.4颜色转换

  • 将子结点的颜色由红变黑
  • 将父结点的颜色由黑变红

但是,每次插入之后要将树的根结点置为黑色。

2.5将红链接在树中向上传递

在沿着插入点到根结点的路径上向上移动时,在经过的每个结点中顺序完成以下操作,就能完成插入操作:

  • 如果右子结点是红色的,而左子结点是黑色的,进行左旋。
  • 如果左子结点是红色的,且它的左子结点也是红色的,进行右旋。
  • 如果左右子结点都是红色的,就进行颜色转换。

2.6实现

/**
 * 红黑树
 * @Author: AZhu
 * @Date: 2021/3/5 23:30
 */
public class RedBlackBST<Key extends Comparable<Key>, Value> {

    private static final boolean RED = true;
    private static final boolean BLACK = false;
    
    private Node root;//根结点

    private class Node {
        Key key;//键
        Value value;//值
        Node left, right;//左右子树
        int N;//以该节点为根的树中的结点总数
        boolean color;//指向自己的链接的颜色

        public Node(Key key, Value value, int n, boolean color) {
            this.key = key;
            this.value = value;
            this.N = n;
            this.color = color;
        }
    }

    private boolean isRed(Node x) {
        if (x==null) return false;
        return x.color == RED;
    }

    /**
     * 左旋
     * @param h
     * @return
     */
    private Node rotateLeft(Node h) {
        Node x = h.right;
        h.right = x.left;
        x.left = h;
        x.color = h.color;
        h.color = RED;
        x.N = h.N;
        h.N = size(x.left) + size(x.right) + 1;
        return x;
    }

    /**
     * 右旋
     * @param h
     * @return
     */
    private Node rotateRight(Node h) {
        Node x = h.left;
        h.left = x.right;
        x.right = h;
        x.color = h.color;
        h.color = RED;
        x.N = h.N;
        h.N = size(x.left) + size(x.right) + 1;
        return x;
    }

    /**
     * 返回树的结点数目
     * @return
     */
    public int size() {
        return size(root);
    }
    private int size(Node x) {
        if (x == null) {
            return 0;
        } else {
            return x.N;
        }
    }

    /**
     * 颜色转换
     * @param h
     */
    private void flipColor(Node h) {
        h.color = RED;
        h.left.color = BLACK;
        h.right.color = BLACK;
    }

    /**
     * 查找key对应的value
     * @param key
     * @return
     */
    public Value get(Key key) {
        return get(root, key);
    }
    private Value get(Node x, Key key) {
        if (x==null) return null;
        int cmp = key.compareTo(x.key);//比较
        if (cmp < 0) {
            return get(x.left, key);//到左子树查找
        } else if (cmp > 0) {
            return get(x.right, key);//到右子树查找
        } else {
            return x.value;//找到了
        }
    }

    /**
     * 插入或更新一对键值对
     * @param key
     * @param value
     */
    public void put(Key key, Value value) {
        root = put(root, key, value);
        root.color = BLACK;
    }
    private Node put(Node h, Key key, Value value) {
        if (h==null) return new Node(key, value, 1, RED);//新建结点
        int cmp = key.compareTo(h.key);//比较
        if (cmp < 0) {
            h.left = put(h.left, key, value);//到左子树
        } else if (cmp > 0) {
            h.right = put(h.right, key, value);//到右子树
        } else {
            h.value = value;//更新
        }
        //沿路径返回调整
        if (isRed(h.right) && !isRed(h.left)) {
            h = rotateLeft(h);
        }
        if (isRed(h.left) && isRed(h.left.left)) {
            h = rotateRight(h);
        }
        if (isRed(h.left) && isRed(h.right)) {
            flipColor(h);
        }
        //增加路径上的每一个结点的N值
        h.N = size(h.left) + size(h.right) + 1;
        return h;
    }
    
}

2.7分析

所有基于红黑树的符号表实现都能保证操作的运行时间为对数级别。

一棵大小为N的红黑树的高度不会超过2lgN

红黑树是平衡的,因此查找比二叉查找树更快。

红黑树的有序性相关操作与二叉查找树一致,并且在最坏情况下的运行时间也是对数级别的。

  • 因此,对于大小可能上亿的表,仍能保证在几十次比较之内完成这些操作。

简单的符号表实现的成本总结:

算法最坏情况平均情况是否高效的支持有序性相关的操作
查找插入查找插入
顺序查找NNN/2N
二分查找lgN2NlgNN
二叉查找树NN1.39lgN1.39lgN
红黑树2lgN2lgN1.00lgN1.00lgN

3答疑

Q1:为什么不存在红色右链接和4-结点?

A1:因为这样可以大大减少代码量。

Q2:为什么不在Node类型中用一个数组来表示2-结点和3-结点?

A2:这样的话就是B树了,并且数组带来的额外开销太大了。

已标记关键词 清除标记
相关推荐
算法(第四版) 目录: 第1章  基础  1 1.1 基础编程模型  4 1.1.1 Java程序的基本结构  4 1.1.2 原始数据类型与表达式  6 1.1.3  语句  8 1.1.4  简便记法  9 1.1.5  数组  10 1.1.6  静态方法  12 1.1.7  API  16 1.1.8  字符串  20 1.1.9  输入输出  21 1.1.10  二分查找  28 1.1.11  展望  30 1.2  数据抽象  38 1.2.1  使用抽象数据类型  38 1.2.2  抽象数据类型举例  45 1.2.3  抽象数据类型的实现  52 1.2.4  更多抽象数据类型的实现  55 1.2.5  数据类型的设计  60 1.3  背包、队列和栈  74 1.3.1  API  74 1.3.2  集合类数据类型的实现  81 1.3.3  链表  89 1.3.4  综述  98 1.4  算法分析  108 1.4.1  科学方法  108 1.4.2  观察  108 1.4.3  数学模型  112 1.4.4  增长数量级的分类  117 1.4.5  设计更快的算法  118 1.4.6  倍率实验  121 1.4.7  注意事项  123 1.4.8  处理对于输入的依赖  124 1.4.9  内存  126 1.4.10  展望  129 1.5  案例研究:union-find算法  136 1.5.1  动态连通性  136 1.5.2  实现  140 1.5.3  展望  148 第2章  排序  152 2.1  初级排序算法  153 2.1.1  游戏规则  153 2.1.2  选择排序  155 2.1.3  插入排序  157 2.1.4  排序算法的可视化  159 2.1.5  比较两种排序算法  159 2.1.6  希尔排序  162 2.2  归并排序  170 2.2.1  原地归并的抽象方法  170 2.2.2  自顶向下的归并排序  171 2.2.3  自底向上的归并排序  175 2.2.4  排序算法的复杂度  177 2.3  快速排序  182 2.3.1  基本算法  182 2.3.2  性能特点  185 2.3.3  算法改进  187 2.4  优先队列  195 2.4.1  API  195 2.4.2  初级实现  197 2.4.3  堆的定义  198 2.4.4  堆的算法  199 2.4.5  堆排序  205 2.5  应用  214 2.5.1  将各种数据排序  214 2.5.2  我应该使用哪种排序算法  218 2.5.3  问题的归约  219 2.5.4  排序应用一览  221 第3章 查找  227 3.1 符号表  228 3.1.1 API  228 3.1.2 有序符号表  230 3.1.3 用例举例  233 3.1.4 无序链表中的顺序查找  235 3.1.5 有序数组中的二分查找  238 3.1.6 对二分查找的分析  242 3.1.7 预览  244 3.2 二叉查找  250 3.2.1 基本实现  250 3.2.2 分析  255 3.2.3 有序性相关的方法与删除操作  257 3.3 平衡查找  269 3.3.1 2-3查找  269 3.3.2 红黑二叉查找  275 3.3.3 实现  280 3.3.4 删除操作  282 3.3.5 红黑的性质  284 3.4 散列表  293 3.4.1 散列函数  293 3.4.2 基于拉链法的散列表  297 3.4.3 基于线性探测法的散列表  300 3.4.4 调整数组大小  304 3.4.5 内存使用  306 3.5 应用  312 3.5.1 我应该使用符号表的哪种实现  312 3.5.2 集合的API  313 3.5.3 字典类用例  315 3.5.4 索引类用例  318 3.5.5 稀疏向量  322 第4章  图  329 4.1  无向图  331 4.1.1  术语表  331 4.1.2  表示无向图的数据类型  333 4.1.3  深度优先搜索  338 4.1.4  寻找路径  342 4.1.5  广度优先搜索  344 4.1.6  连通分量  349 4.1.7  符号图  352 4.1.8  总结  358 4.2  有向图 
算法 Java实现 第四版 PDF格式 中文版 高清扫描版 Robert Sedgewick 著 算法经典书籍 作者: 塞奇威克 (Robert Sedgewick) / 韦恩 (Kevin Wayne) 出版社: 人民邮电出版社 原作名: Algorithms 4th edition 译者: 谢路云 出版年: 2012-10-1 页数: 636 定价: 99.00元 装帧: 平装 丛书: 图灵程序设计丛书 ISBN: 9787115293800 内容简介 · · · · · · 本书全面讲述算法和数据结构的必备知识,具有以下几大特色。  算法领域的经典参考书 Sedgewick畅销著作的最新版,反映了经过几十年演化而成的算法核心知识体系  内容全面 全面论述排序、搜索、图处理和字符串处理的算法和数据结构,涵盖每位程序员应知应会的50种算法  全新修订的代码 全新的Java实现代码,采用模块化的编程风格,所有代码均可供读者使用  与实际应用相结合 在重要的科学、工程和商业应用环境下探讨算法,给出了算法的实际代码,而非同类著作常用的伪代码  富于智力趣味性 简明扼要的内容,用丰富的视觉元素展示的示例,精心设计的代码,详尽的历史和科学背景知识,各种难度的练习,这一切都将使读者手不释卷  科学的方法 用合适的数学模型精确地讨论算法性能,这些模型是在真实环境中得到验证的  与网络相结合 配套网站algs4.cs.princeton.edu提供了本书内容的摘要及相关的代码、测试数据、编程练习、教学课件等资源 作者简介 · · · · · · Robert Sedgewick 斯坦福大学博士,导师为Donald E. Knuth,从1985年开始一直担任普林斯顿大学计算机科学系教授,曾任该系主任,也是Adobe Systems公司董事会成员,曾在Xerox PARC、国防分析研究所(Institute for Defense Analyses)和法国国家信息与自动化研究所(INRIA)从事研究工作。他的研究方向包括解析组合学、数据结构和算法的分析与设计、程序可视化等。 Kevin Wayne 康奈尔大学博士,普林斯顿大学计算机科学系高级讲师,研究方向包括算法的设计、分析和实现,特别是图和离散优化。 目录 · · · · · · 目录 第1章  基础  1 1.1 基础编程模型  4 1.1.1 Java程序的基本结构  4 1.1.2 原始数据类型与表达式  6 1.1.3  语句  8 1.1.4  简便记法  9 1.1.5  数组  10 1.1.6  静态方法  12 1.1.7  API  16 1.1.8  字符串  20 1.1.9  输入输出  21 1.1.10  二分查找  28 1.1.11  展望  30 1.2  数据抽象  38 1.2.1  使用抽象数据类型  38 1.2.2  抽象数据类型举例  45 1.2.3  抽象数据类型的实现  52 1.2.4  更多抽象数据类型的实现  55 1.2.5  数据类型的设计  60 1.3  背包、队列和栈  74 1.3.1  API  74 1.3.2  集合类数据类型的实现  81 1.3.3  链表  89 1.3.4  综述  98 1.4  算法分析  108 1.4.1  科学方法  108 1.4.2  观察  108 1.4.3  数学模型  112 1.4.4  增长数量级的分类  117 1.4.5  设计更快的算法  118 1.4.6  倍率实验  121 1.4.7  注意事项  123 1.4.8  处理对于输入的依赖  124 1.4.9  内存  126 1.4.10  展望  129 1.5  案例研究:union-find算法  136 1.5.1  动态连通性  136 1.5.2  实现  140 1.5.3  展望  148 第2章  排序  152 2.1  初级排序算法  153 2.1.1  游戏规则  153 2.1.2  选择排序  155 2.1.3  插入排序  157 2.1.4  排序算法的可视化  159 2.1.5  比较两种排序算法  159 2.1.6  希尔排序  162 2.2  归并排序  170 2.2.1  原地归并的抽象方法  170 2.2.2  自顶向下的归并排序  171 2.2.3  自底向上的归并排序  175 2.2.4  排序算法的复杂度  177 2.3  快速排序  182 2.3.1  基本算法  182 2.3.2  性能特点  185 2.3.3  算法改进  187 2.4  优先队列  195 2.4.1  API  195 2.
©️2020 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页