open-courses
  • 公开课笔记
  • CMU 15-445/645 Database Systems
    • Relational Data Model
    • Advanced SQL
    • Database Storage
    • Buffer Pools
    • Hash Tables
    • Tree Indexes
    • Index Concurrency Control
    • Query Processing
    • Sorting&Aggregations
    • Join Algorithms
    • Query Optimization
    • Parallel Execution
    • Embedded Database Logic
    • Concurrency Control Theory
    • Two Phase Locking
    • Timestamp Ordering Concurrency Control
    • Multi-Version Concurrency Control
    • Logging Schemes
    • Database Recovery
    • Introduction to Distributed Databases
    • Distributed OLTP Databases
    • Distributed OLAP Databases
  • UCB - CS162
    • OS intro
    • Introduction to the Process
    • Processes, Fork, I/O, Files
    • I/O Continued, Sockets, Networking
    • Concurrency: Processes & Threads
    • Cooperating Threads, Synchronization
    • Semaphores, Condition Variables, Readers/Writers
    • Scheduling
    • Resource Contention & Deadlock
    • Address Translation, Caching
    • File System (18,19,20)
    • Distributed Systems, Networking, TCP/IP, RPC (21,22)
    • Distributed Storage, Key-Value Stores, Security (23)
    • Security & Cloud Computing (24)
    • Topic: Ensuring Data Reaches Disk
  • MIT - 6.006
    • Sequence and Set Interface
    • Data Structure for Dynamic Sequence Interface
    • Computation Complexity
    • Algorithms and Computation
    • Structure Of Computation
    • Graph & Search
    • Tree & Search
    • Weighted Shortest Paths
    • String Matching, Karp-Rabin
    • Priority Queue Interface & Implementation
    • Dictionary Problem & Implementation
    • Sorting
    • Dynamic Programming
    • Backtracking
    • Self-Balancing Tree
  • MIT - 6.824
    • 2PC & 3PC
    • Introduction and MapReduce
    • RPC and Threads
    • Primary/Backup Replication
    • Lab: Primary/Backup Key/Value Service
    • Google File System (GFS)
    • Raft
    • Lab: Raft - Leader Election
    • Lab: Raft - Log Replication
  • Stanford-CS107
    • 原始数据类型及相互转化
    • 指鹿为马
    • 泛型函数
    • 泛型栈
    • 运行时内存结构
    • 从 C 到汇编
    • 函数的活动记录
    • C 与 C++ 代码生成
    • 编译的预处理过程
    • 编译的链接过程
    • 函数的活动记录续、并发
    • 从顺序到并发和并行
    • 信号量与多线程 1
    • 信号量与多线程 2
    • 复杂多线程问题
    • 函数式编程 - Scheme 1
    • 函数式编程 - Scheme 2
    • 函数式编程 - Scheme 3
    • 函数式编程 - Scheme 4
    • 函数式编程 - Scheme 5
    • Python 基础
  • MIT - 6.001 - SICP
    • 什么是程序
    • 程序抽象
    • 替代模型
    • 时间/空间复杂度
    • 数据抽象
    • 高阶函数
    • Symbol
    • 数据驱动编程与防御式编程
    • 数据抽象中的效率与可读性
    • 数据修改
    • 环境模型
    • 面向对象-消息传递
    • 面向对象 - Scheme 实现
    • 构建 Scheme 解释器
    • Eval-Apply Loop
    • Normal Order (Lazy) Evaluation
    • 通用机
    • 寄存器机器
    • 子程序、栈与递归
    • 在寄存器机器中执行
    • 内存管理
  • MIT - 6.046
    • Randomized Algorithms
    • Skip Lists
  • System Design
    • Twitter
    • Cache Consistency & Coherence
  • DDIA 笔记
    • Replication
    • Transactions
    • The Trouble with Distributed Systems
    • Consistency & Consensus
  • Papers We Love
    • Consistent Hashing and Random Trees (1997)
    • Dynamic Hash Tables (1988)
    • LFU Implementation With O(1) Complexity (2010)
    • Time, Clocks, and the Ordering of Events in a Distributed System (1978)
    • Dapper, a Large-Scale Distributed Systems Tracing Infrastructure (2010)
    • Gorilla: A Fast, Scalable, In-Memory Time Series Database (2015)
  • Release It 笔记
    • Anti-patterns & Patterns in Microservice Architecture
  • Database Design
    • Log Structured Merge (LSM) Tree & Usages in KV Stores
    • Prometheus
Powered by GitBook
On this page
  • AVL Tree
  • Operations
  • Red-Black Tree
  • Operations
  • B Tree
  • Operations
  1. MIT - 6.006

Self-Balancing Tree

PreviousBacktrackingNext2PC & 3PC

Last updated 6 years ago

提纲:

  • AVL Tree

  • Red-black Tree

  • B Tree

  • B+ Tree

AVL Tree

AVL Tree 是一种自平衡二叉搜索树(self-balancing BST),数中任意节点的左子树与右子树的高度差小于或等于 1,举例如下:

返例如下:

图中节点 8 的左子树高度为 3,右子树高度为 1,差为 2。

Operations

  • Insertion

  • Deletion

Insertion

每次插入新节点,先按普通二叉树的 insertion 方式插入,若树中所有节点都满足 AVL Tree 的规定,则插入完毕;若树中有节点不满足 AVL Tree 的规定,则根据情况作出相应的调整。可能出现的情况有 4 种:

i) left - left:

ii) left - right:

iii) right - right:

iv) right - left:

代码实现如下:

AVL.py
class TreeNode(object):
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None
        self.height = 1


class AVLTree(object):
    def insert(self, root, key):
        # Step 1 - Perform normal BST
        if not root:
            return TreeNode(key)
        elif key < root.val:
            root.left = self.insert(root.left, key)
        else:
            root.right = self.insert(root.right, key)
        
        # Step 2 - Update the height of the ancestor node
        root.height = 1 + max(self.getHeight(root.left),
                              self.getHeight(root.right))
        # Step 3 - Get the balance factor
        balance = self.getBalance(root)
        # Step 4 - If the node is unbalanced,
        # then try out the 4 cases
        
        # Case 1 - Left Left
        if balance > 1 and key < root.left.val:
            return self.rightRotate(root)
        # Case 2 - Right Right
        if balance < -1 and key > root.right.val:
            return self.leftRotate(root)
        # Case 3 - Left Right
        if balance > 1 and key > root.right.val:
            root.left = self.leftRotate(root.left)
            return self.rightRotate(root)
        # Case 4 - Right Left
        if balance < -1 and key < root.right.val:
            self.right = self.rightRotate(root.right)
            return self.leftRotate(root)
        return root
    
    def leftRotate(self, z):
        y = z.right
        T = y.left
        
        y.left = z
        z.right = T
        # Update heights
        z.height = 1 + max(self.getHeight(z.left),
                           self.getHeight(z.right))
        y.height = 1 + max(self.getHeight(y.left),
                           self.getHeight(y.right))
        return y
    
    def rightRotate(self, z):
        y = z.left
        T = y.right
        
        y.right = z
        z.left = T
        
        # Update heights
        z.height = 1 + max(self.getHeight(z.left),
                           self.getHeight(z.right))
        y.height = 1 + max(self.getHeight(y.left),
                           self.getHeight(y.right))
        return y
        
    def getHeight(self, root):
        if not root:
            return 0
        return root.height
    
    def getBalance(self, root):
        if not root:
            return 0
        return self.getHeight(root.left) - self.getHeight(root.right)

Deletion

每次删除节点,先按普通二叉树的 deletion 方式删除,若树中所有节点都满足 AVL Tree 的规定,则删除完毕;若树中有节点不满足 AVL Tree 的规定,则根据情况作出相应的调整。可能出现的情况有 4 种,与 insertion 中的 4 种情况相同,代码实现如下:

class AVLTree(object):
    def delete(self, root, key):
        # Step 1 - Perform standard BST delete
        if not root:
            return root
        elif key < root.val:
            root.left = self.delete(root.left, key)
        elif key > root.val:
            root.right = self.delete(root.right, key)
        else:
            if root.left is None:
                tmp = root.right
                root = None
                return tmp
            elif root.right is None:
                tmp = root.left
                root = None
                return tmp
            tmp = self.getMinValueNode(root.right)
            root.val = tmp.val
            root.right = self.delete(root.right, tmp.val)
        # If the tree has only one node, simply return it
        if root is None:
            return root
        
        # Step 2 - Update the height of the ancestor node
        root.height = 1 + max(self.getHeight(root.left),
                              self.getHeight(root.right))
        
        # Step 3 - Get the balance factor
        balance = self.getBalance(root)
        
        # Step 4 - If the node is unbalanced,  
        # then try out the 4 cases 
        # Case 1 - Left Left 
        if balance > 1 and self.getBalance(root.left) >= 0: 
            return self.rightRotate(root) 
  
        # Case 2 - Right Right 
        if balance < -1 and self.getBalance(root.right) <= 0: 
            return self.leftRotate(root) 
  
        # Case 3 - Left Right 
        if balance > 1 and self.getBalance(root.left) < 0: 
            root.left = self.leftRotate(root.left) 
            return self.rightRotate(root) 
  
        # Case 4 - Right Left 
        if balance < -1 and self.getBalance(root.right) > 0: 
            root.right = self.rightRotate(root.right) 
            return self.leftRotate(root) 
  
        return root 

Red-Black Tree

Red-Black Tree 与 AVL 一样是自平衡二叉搜索树(self-balancing BST),它的结构要求:

  • 每个 node 都有一种颜色,红或者黑

  • root node 永远是黑色

  • 不能出现相邻的红色 nodes,即一个红色 node 的 parent 和 child 都必须是黑色

  • 从任意 node 出发到所有 leaf node 的路径上包含的黑色 nodes 数量相等

一棵典型的 Red-Black Tree 举例如下:

它与 AVL Tree 相比:

AVL Tree 在 insert 和 delete 操作中会出现更多的 rotations,因此如果应用的 insert 和 delete 操作频繁,则 Red-Black Tree 更合适;若应用的 search 操作较多,insert 和 delete 操作较少,则 AVL Tree 更合适。

值得注意的是:

  • 一棵只含黑色 nodes 的树有可能是 Red-Black Tree,但它必须是完美平衡树(Perfect Balanced Tree)

  • 存在一棵 Red-Black Tree 不满足 AVL Tree 的性质,如上图中的树

Operations

  • Insertion

  • Deletion

Insertion

略

Deletion

略

B Tree

B Tree 是自平衡搜索树 (self-balancing search tree),与 AVL Tree 以及 Red-Black Tree 不同,它不是二叉搜索树,它的使用场景主要在于整棵树无法一次性载入到内存中的场景,当数据需要在 memory 与 disk 之间传输时,各个操作的时间瓶颈就出现在了 disk I/O 上。因此,B Tree 的设计思想是尽可能地矮,使得通往目标 node 的路径尽可能短,从而减少 disk I/O 次数。通常 B Tree 的每个 node 大小为一个 page/block。

B Tree 的主要性质有:

  • 所有的 leaf nodes 都在同一 level

  • B Tree 的类型由 minimum degree --- t 来定义,t 的取值取决于 page/block 大小

  • 除 root node 外的 nodes 都至少包含 k-1 个 keys,root node 至少包含 1 个 key

  • 所有 nodes 至多包含 2t - 1 个 keys

  • 每个 node 的 children 数量比 keys 数量多 1

  • 一个 node 内部的所有 keys 按升序排列,在 k1 与 k2 之间的所有 keys 都包含在 k1、k2 之间的 child node 中

一棵典型的 B Tree 如下图所示:

Operations

  • Search

  • Traverse

  • Insertion

  • Deletion

Search

与其它搜索树的 search 操作类似,递归地从 root node 开始往下搜索,找到合适的 child node,若发现相应的 key 则返回结果,若知道 leaf node 都没找到, 则返回 NULL。

Traverse

traverse 与 inorder traversal 类似,从左下角的 leaf node 开始,inorder traverse 所有 node。

Insertion

与 BST 类似,从 root node 开始往下搜索,直到 leaf nodes 位置,在路径上每遇到一个 node,都先检查该 node 是否已经 full,若是,则先将其分裂成两个 nodes:

  1. 初始化 x 为 root

  2. 当 x 不是 leaf node 时

    a. 找到 x 中与 key 对应的 child,记为 y

    b. 如果 y 不满,则修改 x 为 y

    c. 如果 y 已满,则将 y 分裂,根据 key 的大小将 x 修改为 y 分裂后的其中一个 node

  3. 循环执行第 2 步,直到 x 为 leaf 为止,这时 x 肯定有足够的空间,直接插入 key 即可

这种实现采用主动分裂的策略,但有可能做出不必要的分裂操作。

Deletion

deletion 操作比较复杂,考虑的情况也比较多,有可能需要:

  • 直接删除

  • parent 中的某 key 被删除后拆借 child 中的 key

  • parent 中的某 key 被删除后合并 children

一些可能性如下图所示:

AVL 提出的原因在于解决普通二叉树可能出现的降格问题,保证在二叉树中的搜索、插入、删除操作的复杂度都能控制在 O(logn)O(logn)O(logn) 的范围内。

Red-Black Tree 提出的原因与 AVL Tree 相同,都在于解决普通二叉树可能出现的降格问题,保证在二叉树中的搜索、插入、删除操作的复杂度都能控制在 O(logn)O(logn)O(logn) 的范围内。

B Tree 的所有操作复杂度都为 O(logn)O(logn)O(logn)