【Algorithms 4】算法(第4版)学习笔记 11 - 3.3 平衡查找树(下篇)

前言

由于本章节的内容很多,所以分成了上下两篇,本篇内容比较多,主要包括:左倾红黑树 (以下简称 红黑树)以及 B-树(为了避免有人把连接符 - 看成 ,所以统一写成 B树)。

在学习本章节前,建议先学习上一章节关于 BST(二叉搜索树)的内容,且不要跳过上篇关于 2-3 树的介绍,非常精彩且有助于学习本篇内容。

参考目录

  • B站 普林斯顿大学《Algorithms》视频课
    (请自行搜索。主要以该视频课顺序来进行笔记整理,课程讲述的教授本人是该书原版作者之一 Robert Sedgewick。)
  • 微信读书《算法(第4版)》
    (本文主要内容来自《3.3 平衡查找树》)
  • 官方网站
    (有书本配套的内容以及代码)

学习笔记

注1:下面引用内容如无注明出处,均是书中摘录。
注2:所有 demo 演示均为视频 PPT demo 截图。
注3:如果 PPT 截图中没有翻译,会在下面进行汉化翻译,因为内容比较多,本文不再一一说明。
注4:由于以下内容是连接上篇的,所以目录也是连贯的,不从 1 开始。

2:红黑二叉搜索树

首先来看看 Prof. Sedgewick 的开篇语:

Now,we’ll look at red black BSTs which is a simple data structure that allows us to implement 2-3 tree with very little extra code beyond the basic binary search tree code.
现在,我们将关注红黑BST(二叉搜索树),它是一个简单且高效的数据结构。借助红黑树,我们仅需在基础的BST代码基础上添加很少量额外代码,就能实现对 2-3 树的操作支持。

上篇我们没有对 2-3 树进行代码实现,因为比较复杂,且有更简单高效的实现 —— 即本篇的红黑树。

2.1:左倾红黑树

2.1.1:定义

在开始正式内容之前,Prof. Sedgewick 说了这样一段话(值得学习):

On a personal note, I wrote a research paper on this topic in 1979 with Leo Givas and we thought we pretty well understood these data structures at that time and people around the world use them in implementing various different systems. But just a few years ago for this course I found a much simpler implementation of red-black trees and this is just the a case study showing that there are simple algorithms still out there waiting to be discovered and this is one of them that we’re going to talk about.
从个人角度讲,1979年时我与 Leo Givas 共同撰写了一篇关于这一主题的研究论文,并认为当时我们已经相当透彻地理解了这些数据结构。世界各地的人们在实现各种不同的系统时都在使用它们。然而就在几年前,为了这门课程,我发现了一种红黑树更为简洁的实现方式。这就是一个案例研究,表明仍然有许多简单且尚未被发现的算法等待着我们去探索。而我们现在要讨论的就是其中之一。

简化的方式在后文中会再作说明。

在这里插入图片描述

(源自Guibas-Sedgewick 1979年及Sedgewick 2007年的研究)
1、将 2-3 树以 BST 的形式进行表示。
2、使用“内部”的左倾链接作为 3-节点之间的“粘合剂”。

在这里插入图片描述

2.1.2:等价定义

在这里插入图片描述

2.1.3:红黑树与 2-3 树的一一对应

(截图自官网)
在这里插入图片描述

2.2:红黑树实现

2.2.1:搜索实现

在这里插入图片描述

实现代码和基本 BST 一样。

edu.princeton.cs.algs4.RedBlackBST#get

在这里插入图片描述

2.2.2:红黑树表示

在这里插入图片描述

每个节点都恰好通过一条链接(来自其父节点)进行指向 =>
可以在节点中编码链接的颜色信息。

2.3:三种基本操作(重要!)

对于红黑树而言,在进行一些变体操作(例如插入和删除)后,为了保持树的平衡,因此需要以下三种基本操作。

所以每张 PPT 下面都有一句话:

不变式:保持对称顺序和完美的黑色平衡。

理解并熟练掌握这三种基本操作之后,对于后面不同形式的红黑树操作都可以灵活应对。

2.3.1:左旋转

在这里插入图片描述

左旋转:(临时)将一个右倾的红色链接调整为左倾。
不变式:保持对称顺序和完美的黑色平衡。

在这里插入图片描述

edu.princeton.cs.algs4.RedBlackBST#rotateLeft

在这里插入图片描述

2.3.2:右旋转

在这里插入图片描述

右旋转:将一个左倾的红色链接调整为(临时)右倾。
不变式:保持对称顺序和完美的黑色平衡。

在这里插入图片描述

edu.princeton.cs.algs4.RedBlackBST#rotateRight

在这里插入图片描述

2.3.3:颜色翻转

在这里插入图片描述

颜色翻转:重新着色以拆分一个(临时)的 4-节点。
不变式:保持对称顺序和完美的黑色平衡。

在这里插入图片描述

edu.princeton.cs.algs4.RedBlackBST#flipColors

在这里插入图片描述

Q:为什么需要翻转颜色?
A: #2.1.2 等价定义中有一个性质:没有任何一个结点同时和两条红链接相连。

后面再具体讲为什么会出现两条红链接,简单来说是:在红黑树中,插入和删除操作可能会破坏这一性质。

2.4:插入操作

在这里插入图片描述

基本策略:通过保持与2-3树的一一对应关系,将基本操作应用于红黑树。

2.4.1:插入:树只有 1 个节点

热身1:插入到一棵只有 1 个节点的树中。

在这里插入图片描述

2.4.2:插入:2-节点

在这里插入图片描述

案例1:在底部 2-节点中插入新节点。

  • 执行标准BST(二叉搜索树)插入操作;将新插入的链接设置为红色。
  • 如果新插入的红色链接是右链接,则进行左旋操作。

在这里插入图片描述

2.4.3:插入:树只有 2 个节点

热身2:插入到一棵只有 2 个节点的树中。

在这里插入图片描述

2.4.4:插入:3-节点 1

在这里插入图片描述

案例2:在底部 3-节点中插入新节点。

  • 执行标准BST(二叉搜索树)插入操作;将新插入的链接设置为红色。
  • 如果需要,进行旋转以平衡产生的 4-节点。
  • 翻转颜色以便将红色链接上移一层。
  • 如果需要,进行旋转使节点向左倾斜。

在这里插入图片描述

2.4.5:插入:3-节点 2

在这里插入图片描述

案例2:在底部 3-节点中插入新节点。

  • 执行标准BST(二叉搜索树)插入操作;将新插入的链接设置为红色。
  • 如果需要,进行旋转以平衡产生的 4-节点。
  • 翻转颜色以便将红色链接上移一层。
  • 如果需要,进行旋转使节点向左倾斜。
  • 重复案例 1 或者案例 2,向上传递直至树顶(如果需要)。

在这里插入图片描述

2.4.6:插入:红黑树构造

步骤太长,这里直接用书里的步骤插图。

在这里插入图片描述
图3.3.24 红黑树的构造轨迹

插入轨迹的变换都是基于三种基本的操作:左旋转、右旋转、颜色翻转,建议参考前面的插入案例自行想象一下节点插入的过程。

2.4.7:插入:Java 实现

在这里插入图片描述

书里面的总结:

在这里插入图片描述

示意图:

在这里插入图片描述

这张图实际上列出了所有可能出现的情况以及需要进行的操作,非常重要。

edu.princeton.cs.algs4.RedBlackBST#put

在这里插入图片描述

在这里插入图片描述

对比 BST 插入实现,只需几行额外的代码就提供了近乎完美的平衡。

可以回头看看 BST 插入的相关代码,这里的实现真的是:优雅,实在是太优雅了!!!

对此,Prof. Sedgewick 作了如下说明(Respect Prof.!):

Typical implementations of red-black trees that do not use this recursive strategy wind up having lots of cases depending on whether left or right or double rotate to the left or double rotate to the right can be critical of this code because my own was this way for the first three editions of the book. And it’s only in this edition that we figured out how to make the code this simple.
通常情况下,那些不采用递归策略实现红黑树的实例,会因为需要根据具体情况判断是进行左旋、右旋还是双左旋、双右旋而包含大量的条件分支。对于这类代码,人们可能会有所诟病,因为我在本书前三个版本中所给出的实现正是如此。直到这个版本,我们才成功地简化了代码,使其变得如此简洁。

注意前面一句:

通常情况下,那些不采用递归策略实现红黑树的实例,会因为需要根据具体情况判断是进行左旋、右旋还是双左旋、双右旋而包含大量的条件分支。

这点我在《漫画算法2:小灰的算法进阶》关于红黑树的章节中看到了,针对不同的形式进行了不同的判断和处理,因而代码很长,感兴趣的朋友可以自行查阅,这里不再引用。

官网也有插入操作的 可视化图

在这里插入图片描述

2.5:左倾红黑树的平衡

在这里插入图片描述

这里的证明和书本的稍微有点不同,贴一下书里面的:

在这里插入图片描述

2.6:符号表实现小结

在这里插入图片描述

2.7:小故事

关于红黑树的命名,教授给出了这样一个回答(原话没摘录,维基百科有相关的说明):

之所以选择“红色”是因为这是作者在帕罗奥多研究中心公司(Xerox PARC)工作时用彩色激光打印机可以产生的最好看的颜色。
(via@维基百科:红黑树)

说完红黑树的命名,教授又讲了一个小故事:

在这里插入图片描述

2.8:小补充:有关删除操作

教授没有对删除操作进行讲解,书里面也是简单带过:

在这里插入图片描述

需要注意的还是:通过三种基本操作保持树的平衡性。

edu.princeton.cs.algs4.RedBlackBST#delete

在这里插入图片描述

私有方法太长,我直接贴一下源码:

	// delete the key-value pair with the given key rooted at h
    private Node delete(Node h, Key key) {
        // assert get(h, key) != null;

        if (key.compareTo(h.key) < 0)  {
            if (!isRed(h.left) && !isRed(h.left.left))
                h = moveRedLeft(h);
            h.left = delete(h.left, key);
        }
        else {
            if (isRed(h.left))
                h = rotateRight(h);
            if (key.compareTo(h.key) == 0 && (h.right == null))
                return null;
            if (!isRed(h.right) && !isRed(h.right.left))
                h = moveRedRight(h);
            if (key.compareTo(h.key) == 0) {
                Node x = min(h.right);
                h.key = x.key;
                h.val = x.val;
                // h.val = get(h.right, min(h.right).key);
                // h.key = min(h.right).key;
                h.right = deleteMin(h.right);
            }
            else h.right = delete(h.right, key);
        }
        return balance(h);
    }

3:B树

本小节内容对应书本第六章《6.0.2 B-树》。

3.0:文件系统模型

在这里插入图片描述

页(Page):连续的数据块(例如,一个文件或4096字节的区块)。
探查(Probe):对一个页面进行的首次访问(例如,从磁盘读取到内存中的首次访问)。

特性(Property):执行一次探查所需的时间远大于访问一页内部数据所需的时间。
成本模型(Cost model):探查次数。
目标(Goal):通过使用尽可能少的探查次数来访问数据。

3.1:定义

在这里插入图片描述

B树:通过允许每个节点最多存储 M-1 个 键-链接对 来推广 2-3 树(选择尽可能大的M值,以便M个链接能够适应一页,例如,M=1024)。

  • 根节点至少包含 2 个 键-链接对。
  • 其他内部节点至少包含 M/2 个 键-链接对。
  • 外部节点(也称为叶节点)存储客户端的键数据。
  • 内部节点包含键的副本,用于引导搜索过程。

在这里插入图片描述

对于哨兵键 *

为了方便这里使用了一个特殊的哨兵键,它小于其他所有键。一开始B-树只含有一个根节点,而根结点在初始化时仅含有该哨兵键。

3.2:搜索操作

在这里插入图片描述

  • 从根节点开始搜索。
  • 找到与搜索键对应的区间,并沿相应链接进入子树。
  • 搜索在外部节点(即叶节点)处终止。

在这里插入图片描述

3.3:插入操作

在这里插入图片描述

  • 搜索新键的位置。
  • 从底部插入新键。
  • 在沿着树向上遍历的过程中,遇到包含M个键-链接对的节点时进行分裂操作。

在这里插入图片描述

3.4:B树性能

在这里插入图片描述

命题:在一个具有 M 阶、包含 N 个键的B树中,搜索或插入操作所需的探查次数介于 log_M-1_N 和 log_M/2_N 之间。

证明:所有内部节点(除根节点外)拥有介于 M/2 和 M-1 之间的链接数。在实际应用中,当 M=1024 且 N=620亿 时,探查次数最多为 4 次(即log_M/2_N <= 4)。

优化策略:始终将根页面保留在内存中。

在这里插入图片描述

3.5:实际应用

在这里插入图片描述

红黑树被广泛用作系统符号表实现。

  • Java语言中:java.util.TreeMapjava.util.TreeSet
  • C++ STL中:mapmultimapmultiset
  • Linux内核中:完全公平调度器(Completely Fair Scheduler),使用linux/rbtree.h头文件实现的红黑树数据结构。
  • Emacs编辑器中:保守栈扫描(Conservative Stack Scanning)技术也涉及红黑树的应用。

B树及其变种(如B+树、B*树、B#树等)被广泛应用在文件系统和数据库领域。

  • 操作系统:
    • Windows:NTFS文件系统。
    • Mac OS:HFS和HFS+文件系统。
    • Linux:ReiserFS、XFS、Ext3FS、JFS文件系统。
  • 数据库管理系统:
    • Oracle
    • DB2
    • INGRES
    • SQL Server
    • PostgreSQL

这些平衡树及变种在计算机编程中扮演着关键角色,它们提供了一种组织和检索大量数据的有效方法,确保了高效的查找、插入和删除操作。

(完)

  • 36
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MichelleChung

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值