红黑树的原理及python简单实现

4 篇文章 0 订阅
1 篇文章 0 订阅

了解红黑树之前需要先了解一种树:2-3查找树。

2-3查找树

为了保证二叉查找树的平衡性,需要一些灵活性,因为我们允许树中的一个结点保存多个键。确切的说,将一棵标准的二叉查找树中的结点称为2-结点(含有一个键和两条链),而现在我们引入3-结点,它含有两个键和三条链。2-结点和3-结点中的每条链都对应着其中保存的键所分割产生的一个区间。
定义:一颗2-3查找树要么为空,要么满足下面两个要求:

  • 2-结点:含有一个键(及其对应的值)和两条链,左连接指向2-3树中的键都小于该结点,右连接指向的2-3树中的键都大于该结点。
  • 3-结点:含有两个键(及其对应的值)和三条链,左连接指向的2-3树中的键都小于该结点,中连接指向的2-3树中的键都位于该节点的两个键之间,右连接指向的2-3树中的键大于该结点。

2-3查找树实现起来比较复杂,在某些情况下插入后的平衡操作可能会使得效率降低。但是2-3查找树作为一种比较重要的概念和思路对于红黑树,B树和B+树非常重要。

红黑树

红黑树主要对2-3树进行编码,红黑树背后的基本思想是用标准的二叉查找树(完全由2-结点构成)和一些额外的信息(替换了3-结点)来表示2-3树。将树中的连接分为两种类型:

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

确切的说,我们将3-结点表示为由一条左斜的红色链接(两个2-结点 其中之一是另一个的左子结点)链接的两个2-结点。这种表示法的一个优点是:我么无需修改就可以直接使用标准的二叉查找树的get方法。
定义:红黑树是含有红黑连接并满足下列条件的二叉查找树:

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

平衡化:

  • 左旋:当某个结点的左子结点为黑色,右子结点为红色,此时需要左旋。
  • 右旋:当某个结点的右子结点是红色,且左子结点的左子结点也是红色,需要右旋。
  • 颜色反转:当一个结点的左子结点和右子结点的color都为RED时,也就是出现了临时的4-结点,此时只需要把左子结点和右子结点的颜色变为BLACK,同时让当前结点的颜色变为RED即可。

向单个2-结点中插入新键
一棵只含有一个键的红黑树只含有一个2-结点。插入另一个键后,我们就马上将他们旋转:

  • 如果新键小于当前结点的键,我们只需要新增一个红色结点即可,新的红黑树和单个3-结点完全等价。
  • 如果新键大于当前结点的键,那么新增的红色结点将会产生一条红色的右链接,此时我们需要通过左旋把红色右链接变成左链接,插入操作才算完成。形成新的红黑树依然和3-结点等价,其中含有两个键,一条红色链接。

向底部的2-结点插入新键
用和二叉查找树相同的方式向一棵红黑树中插入一个新键,会在树的底部新增一个结点(可以保证有序性),唯一区别的地方是我们会用红链接将新结点和它的父结点相连。如果他的父结点是一个2-结点,那么刚才的方式仍然适用。
向一棵双键树(即一个3-结点)中插入新键
这种情况又可以分为三种子情况:

  • 新键大于原树中的两个键。
  • 新键小于原树中的两个键。
  • 新键介于原树中的两个键。

根节点的颜色总是黑色的。
python简单实现红黑树及插入功能及查询功能:

## 红黑树的实现

RED = "red"
BLACK = "black"


class Node(object):
    def __init__(self, key, value):
        self.key = key
        self.value = value
        self.right = None
        self.left = None
        self.color = RED


class RedBlackTree(object):
    def __init__(self):
        self.root = None
        self.N = 0

    ## 获取树中元素的个数
    def size(self):
        return self.N

    ## 判断当前结点的父指向链接是否为红色
    def isRed(self, x):
        if x:
            return x.color == RED
        return False
    ## 左旋转  当某个结点的左子节点为黑色,右子节点为红色,此时需要左旋
    def rotateLeft(self, h):
        ## 获取h结点的右子结点,标识为x
        x = h.right

        ## 让x结点的左子结点成为h结点的右子结点
        h.right = x.left
        ## 让h成为x结点的左子结点
        x.left = h
        ## 让x结点的color属性等于h结点的color属性
        x.color = h.color
        ## 让h结点的color属性变为红色
        h.color = RED
        return x  ## 左旋之后父链接指向了x,所以返回x

    ## 右旋转  当某个结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋
    def rotateRight(self, h):
        ## 获取h结点的左子结点,表示为x
        x = h.left
        ## 让x结点的右子结点成为h结点的左子结点
        h.left = x.right
        ## 让h结点成为x结点的右子结点
        x.right = h
        ## 让x结点的color属性等于h结点的color属性
        x.color = h.color
        ## 让h结点的color属性变为红色
        h.color = RED
        return x

    ## 颜色反转 相当于完成拆分4-结点
    def flipColor(self, h):
        ## 当前结点变为红色
        h.color = RED
        ## 左子结点和右子结点变为黑色
        h.left.color = BLACK
        h.right.color = BLACK
        return h

    ## 在树上插入键值对
    def insert(self, key, value):
        self.root = self._insert(self.root, key, value)
        self.N += 1
        self.root.color = BLACK

    ## 向指定树中插入键值对
    def _insert(self, root, key, value):
        ## 比较root结点和node结点的键的大小
        if root == None:
            return Node(key, value)
        cmp = key - root.key
        if cmp < 0:
            ## 继续往左
            root.left = self._insert(root.left, key, value)
        elif cmp > 0:
            ## 继续往右
            root.right = self._insert(root.right, key, value)
        else:
            ## 发生值的替换
            root.value = value
            self.N -= 1

        ## 进行左旋(当当前结点的左子节点为黑色,右子节点为红色,此时需要左旋)
        if self.isRed(root.right) and not self.isRed(root.left):
            root = self.rotateLeft(root)
        ## 进行右旋(当当前结点的左子结点是红色,且左子结点的左子结点也是红色,需要右旋)
        if self.isRed(root.left) and self.isRed(root.left.left):
            root = self.rotateRight(root)
        ## 进行颜色反转(当前结点的左子结点和右子结点都为红色时,需要进行颜色反转)
        if self.isRed(root.left) and self.isRed(root.right):
            root = self.flipColor(root)
        return root

    ## 根据key,查找对应的值
    def get(self, key):
        return self._get(self.root,key)

    ## 从指定的树中查找key对应的值
    def _get(self, root, key):
        if root == None:
            return None
        ## 比较root结点和key的大小
        cmp = key - root.key
        if cmp < 0:
            return self._get(root.left,key)
        elif cmp > 0:
            return self._get(root.right,key)
        else:
            return root.value

if __name__ == '__main__':
    red_black_tree = RedBlackTree()
    red_black_tree.insert(1,"张三")
    red_black_tree.insert(2,"李四")
    red_black_tree.insert(3,"王五")
    red_black_tree.insert(4,"刘一")
    red_black_tree.insert(5,"刘二")
    red_black_tree.insert(6,"刘三")
    red_black_tree.insert(7,"刘四")
    red_black_tree.insert(8,"刘五")
    red_black_tree.insert(9,"刘六")
    red_black_tree.insert(10,"刘七")
    red_black_tree.insert(10,"刘八")
    r1 = red_black_tree.get(7)
    r2 = red_black_tree.get(10)
    print(r1)
    print(r2)
    n = red_black_tree.size()
    print(n)
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值