如何利用红黑树实现排名?

1. 问题


红黑树是一种自平衡的二叉查找树,它可以在O(logn)时间内执行查找、插入和删除。在c++ STL,linux内核中都有使用。

红黑树本身是有序的,现在问题是对于指定的元素,如何能快速查到它在整个元素集的排名,或者根据排名快速查询对应的元素?

2. 思路


排名分顺序和逆序,这里只讨论顺序的情况。顺序的话排名就是求比当前元素小的元素的个数,根据红黑树的性质,左子树的节点都比根节点小,右子树的节点都比根节点大,求排名就等价于求节点左子树元素的个数。

根据树的递归性质,我们只需要在每个节点增加一个字段count用来统计当前节点子树的个数,同时在红黑树做插入、删除操作的时候更新count字段,就能在O(logn)的时间内查询到该元素的排名。

3. 实现


红黑树节点增加count字段,count[x]表示x节点子节点元素的个数,包括它的左子树,它的右子树和它自己本身。

count[x] = count[left[x]] + count[right[x]] + 1; // x非空
count[x] = 0; // x为空

红黑树旋转的时候,保证count满足我们的定义就可以。

3.1 左旋

如图所示,左旋的时候,只有x和y节点count域会发生变化,只需要修改图中x和y节点对应的count,其余节点不需要修改。

左旋后:

count[x] = count[α] + count[β] + 1
count[y] = count[x] + count[γ] + 1

左旋伪代码:

LEFT-ROTATE(T, x)
    y = right[x]
    right[x] = left[y]
    p[left[y]] = x
    p[y] = p[x]
    count[x] = count[left[x]] + count[left[right[x]]] + 1
    count[right[x]] = count[left[x]] + count[left[right[x]]] + count[right[right[x]]] + 2
    if p[x] == nil
    then root[T] = y
    else if x == left[p[x]]
        then left[p[x]] = y
        else right[p[x]] = y
    left[y] = x
    p[x] = y

3.2 右旋

右旋同样类似于左旋,只需要修改x 和 y节点对应的count。

右旋后:

count[y] = count[γ] + count[β] + 1
count[x] = count[x] + count[α] + 1

右旋伪代码:

RIGHT-ROTATE(T, y)
    x = left[y]
    left[y] = right[x]
    p[right[x]] = y
    p[x] = p[y]
    count[y] = count[right[y]] + count[right[left[y]]] + 1
    count[left[y]] = count[right[y]] + count[left[left[y]]] + count[right[left[y]]] + 2
    if p[y] == nil
    then root[T] = x
    else if y == right[p[y]]
        then right[p[y]] = x
        else left[p[y]] = x
    right[x] = y
    p[y] = x

插入和删除的时候对于count的修改比较简单,只修改节点所有祖先节点的count,插入的时候,我们先按照红黑树的规则插入到指定位置,然后对该节点的所有祖先节点的count都增加1,然后再做平衡调整,删除的时候类似。

3.3 根据排名查询元素

跟红黑树普通的查询类似,只不过用来比较的域换成了count,这里分为三种情况:

1.节点左子树个数 + 1 == rank,表示已经找到需要查询的元素

2.节点左子树个数 + 1 > rank, 表示当前节点左子树个数大于rank - 1,我们需要在左子树中递归查询

3.节点左子树个数 + 1 < rank, 表示当前节点左子树个数大于rank - 1, 我们需要在右字数中查询,注意这个时候需要修改rank值

QUERYBYRANK(T, r)
    y = root[T]
    while y != nil
        if count[left[y]] + 1 == r then
            // find it
            exit
        else if count[left[y]] +1 > r then
            y = left[y]
        else
            r = r - count[left[y]] - 1
            y = right[y]

3.4 查询排名

红黑树普通查询,O(logn)可以查询到指定元素的排名

RANK(T, x)
    y = root[T]
    while y != nil
        if key[x] == key[y] then
            // find it
            r = count[y]
            exit
        else if key[x] < key[y] then
            y = left[y]
        else y = right[y]

4. 总结


插入、删除、查找算法都是在原红黑树的基础上进行简单修改,时间复杂度均为O(logn)。

红黑树增加count扩展后,增加的count操作主要在红黑树的旋转,每次红黑树平衡最多3次旋转,所以对红黑树的性能影响很小,可以用来实现游戏中常见的排行榜功能。但是当元素集合的总量达到一定规模比如千万级,可能会有性能问题,主要消耗在红黑树key的字符串比较上。

原文发布于微信公众号 - 腾讯数据库技术(gh_83eebc796d5d)

原文发表时间:2017-11-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏云霄雨霁

字符串查找----查找算法的选择

29900
来自专栏Java3y

Map集合、散列表、红黑树介绍

20030
来自专栏木子昭的博客

递归并非万能

递归的确简洁, 但性能很差, 因为它进行了大量重复的计算, 如果用递归运算做乘法, 5!*4! = 5*4*3*2*1 * 4*3*2*1显然4!完全可以算...

27450
来自专栏数据结构与算法

P1789 【Mc生存】插火把

题目背景 初一党应该都知道...... 题目描述 话说有一天linyorson在Mc开了一个超平坦世界,他把这个世界看成一个n*n的方阵,现在他有m个火把和k个...

37050
来自专栏自学笔记

Data Structure_图图论带权图

交通运输,社交网络,互联网,工作的安排,闹区活动等等都可以用到图论处理。图可以分成两大类,一类是无向图,就是没有方向的,就好像两个人都互相认识一样,有向图就是单...

8110
来自专栏数据结构与算法

BZOJ4766: 文艺计算姬

Description "奋战三星期,造台计算机"。小W响应号召,花了三星期造了台文艺计算姬。文艺计算姬比普通计算机有更多的艺 术细胞。普通计算机能计算一个带...

33980
来自专栏菩提树下的杨过

数据结构C#版笔记--啥夫曼树(Huffman Tree)与啥夫曼编码(Huffman Encoding)

哈夫曼树Huffman tree 又称最优完全二叉树,切入正题之前,先看几个定义 1、路径 Path 简单点讲,路径就是从一个指定节点走到另一个指定节点所经过的...

28290
来自专栏xingoo, 一个梦想做发明家的程序员

回溯法算法框架

回溯法:有通用解题法 之称,可以系统的搜索一个问题的所有解和任一解,是一个既带有系统性,又带有跳跃性的搜索算法。 算法基本思想:   确定解空间后   从开始节...

35460
来自专栏小樱的经验随笔

约瑟夫问题方法总结

n个人围成一个圈,每个人分别标注为1、2、...、n,要求从1号从1开始报数,报到k的人出圈,接着下一个人又从1开始报数,如此循环,直到只剩最后一个人时,该人即...

37380
来自专栏calmound

HDU 2011 菜鸟杯

A:该题写了很久,一直TLE,主要是枚举到n/2时间复杂度实在太高了,其实嘛,这道题就是因式分解,所以就是i*i=n,也就是sqrt(n) #include<s...

31740

扫码关注云+社区

领取腾讯云代金券