红黑树和平衡二叉树的区别_面试题精选红黑树(c/c++版本)

红黑树的使用场景非常广泛,比如nginx中用来管理timer、epoll中用红黑树管理事件块(文件描述符)、Linux进程调度Completely Fair Scheduler用红黑树管理进程控制块、C++STL中map,set的底层实现全是用的红黑树。掌握红黑树的原理以及使用场景,对于我们面试和工作、以及理解开源代码都是非常有帮助。

二叉树介绍

在关注红黑树之前,首先复习一下二叉树的概念。在数据结构中,二叉树有如下定义:

  1. 二叉树是一种由节点和层组成的结构
  2. 每一层有若干个节点
  3. 第一层只能有一个根节点
  4. 每个节点可以拥有两颗支树,分别被称为左子树(left subtree)和右子树(right subtree)
  5. 对于一个节点,左子树所有的元素都比这个节点存储的元素小,右子树所有的节点都比这个节点存储的元素大

它的结构类似如下的截图:

892bc4301f9347ab5d27adfdad8ff11e.png

那么二叉树被定义为以上的规则,到底有什么用呢?跟传统的链表存储相比,到底有什么优势呢?对于上述的二叉树,如果使用链表存储则表示如下:

74fccfb418e83e60503c3d71bd2a4f16.png

当我们从这个链表中的头部107开始查找元素的时候,对于它的7个成员查找需要的次数如下:

acebd50436f5e7ffd7cbc9cf29eaf79e.png

通过如上表格我们发现,链表中元素的比较次数跟元素在链表中存储的位置正相关,所有元素的最终的平均比较次数等于(1 + 2 + 3 + 4 + 5 + 6 + 7) / 7 = 4

对于二叉树来说,由于二叉树被定义为左子树所有的元素都比这个节点存储的元素小,右子树所有的节点都比这个节点存储的元素大,对于完全二叉树来说,每次比较我们都可以排除掉一半的元素。

比如对于上述二叉树中的元素45来说,第一次跟60比较,小于60,直接排除掉右子树所有的元素(100,67,107),第二次跟55比较,小于55,排除掉55右子树所有的元素(57),最后定位到45,这里只比较了3次。在二叉树中七个成员查找需要的次数如下:

84d153dda41de879ed083d10e8e8f3be.png

所有元素的最终的平均比较次数等于(1 + 2 + 2 + 3 + 3 + 3 + 3) / 7 ≈ 2.4,很直观的发现,二叉树的平均比较次数比链表少。即链表的平均复杂度为O(n),而二叉树的平均复杂度为O(logn)。可以说,二叉树是一种比链表更高效查找的数据结构。

二叉树的弊端

上面已经说过,二叉树是比链表查找更高效的数据结构,但是二叉树的时间复杂度一定比链表低吗?还是争对上述的二叉树中的元素举例,即按45->55->57->60->67->100->107的顺序,把元素插入二叉树当中,插入过程如下图所

示:

68e0a25a57e21dcbe273b2876a3b968e.png

我们发现,当按顺序插入的时候,二叉树变成了一个”瘸子“,这样的只有个一个支数的二叉树,实际上就是一个链表。也就是说,二叉树最坏的情况下,查找的时间复杂度等于链表O(n),这完全失去了二叉树的意义。那么如何避免这种情况,让二叉树能尽可能的“平衡”一点呢?

另外小编还整理了一些各大知名企业BAT精选面试题、需要的朋友可以加qun720209036获取

7959663649e93a9c1c8e2f9119363eb7.png

红黑树的定义

为了让插入的时候,二叉树尽可能的平衡,1972年鲁道夫·贝尔提出红黑树的概念,对于红黑树的定义如下:

  1. 节点是红色或黑色
  2. 根是黑色
  3. 所有叶子都是黑色(叶子是NIL节点)
  4. 每个红色节点必须有两个黑色的子节点(从每个叶子到根的所有路径上不能有两个连续的红色节点)
  5. 从任一节点到其每个叶子(NIL)的所有简单路径都包含相同数目的黑色节点

遵循如上规则的二叉树被称为红黑树,但是为什么要定义成如上规则呢?事实上,如上所有的规则都只想做一件事,让二叉树尽可能的平衡。

  1. 对于定义4,即代表了红黑树中不可能出现两个连续的红色节点,也就是说支树上最多是红黑交替的情况(可以出现连续的黑节点)。
  2. 对于定义5,结合定义4,即代表红黑树中最长路径最多是最短路径的两倍。

争对上述的1,2举例:

e273d95e9acd4f31474cd2182733adb2.png

上图是一个符合红黑树定义的二叉树,叶子(NIL)节点就是一个空节点,表示节点在此位置上没有元素了。在实际的算法实现中NIL不需要考虑,填上NIL节点只是为了判断到此路径终点的NIL节点上,经过多少黑节点。

此红黑树中,根节点到NIL节点,最短路径为55到45(2个黑色节点),最长路径为55到107(2个黑色节点,2个红色节点),最长路径是最短路径的节点数的两倍。如果在往107后面添加一个黑色节点,则不符合定义5,因为多了一个黑色节点。所以红黑树保证了最坏情况和最好情况不会差太多,做到一个相对来说的平衡。

左旋和右旋

对于一个节点N来说,它有一个左节点L,右节点R,如下图所示:

3217733247851d62a88ec95d95f2ce41.png

根据二叉树定义,N > L,N < R,且N为L和R的父节点。那么如果L当儿子当够了,现在L想当N的爸爸(父节点),二叉树该怎么调整?

  1. 因为L < N,根据二叉树定义,把N放入L的右节点处
  2. N的左节点指向节点2

7053cf6541a67ed2d7d8512ba6021ec9.png

上述的过程称为对节点N的右旋,对应的动态图为:

5f2e9ea5f0e7f152cbd9951293545126.gif

同理,如果R想做N的父节点呢?

  1. 因为R > N,根据二叉树定义,把N放入R的左节点处
  2. N的右节点节点指向节点3

21a1a4d7654df5d49da74d0f4871c27e.png

上述的过程称为对节点N的左旋,对应的动态图为:

12682be39be8f924217a8075c1f79cae.gif

其实所谓的左旋右旋,可以理解为节点的子节点想”篡位“,原来的子节点变为现在节点的父节点。

红黑树的插入

复习一下二叉树的插入规则:

  1. 定位到需要插入的位置
  2. 插入元素

二叉树的插入如下图所示:

60d4e167eca6ad40f4b1cd500d6540fe.png

而红黑树比二叉树多了一个规则,插入后重新平衡二叉树,让它符合红黑树的定义。规定新插入的节点一定是红色的,因为如果插入的是黑色的,原来经过插入之前的节点的元素就会多一个黑色节点,调整起来比较麻烦。

下面介绍插入完成之后如何平衡二叉树:

假设刚插入的节点为X,它的父节点为N,祖父节点为P,N的兄弟节点为U,下图列举了一种可能的情况:

说明:红底的代表红节点,黑底的代表黑节点,白底的代表颜色未知的节点(即可黑可红)。

33b7da3bfd6c829fec6b99b2c81ae40d.png

现在需要以X节点的父节点N的颜色作为一个切入点开始讨论:

  1. N为黑色
    这种情况是最简单的,不需要做任何调整,因为新插入的节点X为红色,并不会影响经过X的支路的黑节点数量,之前是多少,现在就是多少。
  2. N为红色此时出现了X和N为两个连续的红节点,需要调整树,让它符合红黑树的特征。对于X,N,P节点的关系,现在这里有四种情况:
第一种:N为P的左节点,X为N的左节点
第二种:N为P的左节点,X为N的右节点
第三种:N为P的右节点,X为N的右节点
第四种:N为P的右节点,X为N的左节点
  1. 观察后不难发现,其实前面两种和后面两种是成镜像的,也就是说分析前两种,后面两种的操作可以效仿前两种。现在我们可以对节点做一下左旋,右旋,节点颜色修改等操作,让树重新符合红黑树特征(由于N为红色,根据红黑树的特征4,P节点一定为黑。)。
  • N为P的左节点,X为N的左节点

45d4ee002c9258f0db2ee9e3716cb409.png
    • 这种情况我们就需要分析U节点的颜色
      ① U节点为红色
      直接把N和U全部设置为黑色,把P设置为红色即可

1701fdfe3b6976443142e11f0a1b7a5e.png


设到达P节点之前,有a个黑色节点。在改变之前,在到达X,1,2,3这四个节点的时候,经过黑色节点的数量如下:

X节点:a + 1
1节点:a + 2
2节点:a + 2
3节点:a + 2
    • 改变之后
X节点:a + 1
1节点:a + 2
2节点:a + 2
3节点:a + 2
    • 可以发现改变之后还是符合红黑树的特征5。上述给了一种分析经过路径的黑节点的数量的一种手段。
      ② U节点为黑色
      首先对P节点进行右旋,然后交换N和P节点的颜色

cb4fff67dc6287a866be2e3ca727df24.png


设到达P节点之前,有a个黑色节点。在改变之前,在到达X,1,2,3这四个节点的时候,经过黑色节点的数量如下 (因为2,3节点的颜色未知,所以用d2,d3代表。比如说如果2为黑,d2则为1,否则为0):

X节点:a + 1
1节点:a + 2
2节点:a + 2 + d2
3节点:a + 2 + d3
    • 改变之后
X节点:a + 1
1节点:a + 2
2节点:a + 2 + d2
3节点:a + 2 + d3
    • 可以发现改变之后还是符合红黑树的特征5。
      N为P的左节点,X为N的右节点
      首先左旋N节点

27455bf8cc2e0cfbe4f22ae25e6ca814.png

设到达P节点之前,有a个黑色节点。在改变之前,在到达1,2,3,4,5这五个节点的时候,经过黑色节点的数量如下 (因为2,3节点的颜色未知,所以用d2,d3代表。比如说如果2为黑,d2则为1,否则为0):

1节点:a + 2
2节点:a + 2 + d2
3节点:a + 2 + d3
4节点:a + 2
5节点:a + 2
    • 改变之后
1节点:a + 2
2节点:a + 2 + d2
3节点:a + 2 + d3
4节点:a + 2
5节点:a + 2
    • 可以看出,并没有改变支路上黑节点的数量。但是N,X还是两个连续的红节点,还要继续操作。观察到上图被红色方框圈出来的东西发现,它就是之前N为P的左节点,X为N的左节点的那种情况,所以可以转到对应的步骤处理。
    • N为P的右节点,X为N的右节点
      对应N为P的左节点,X为N的左节点,把右旋换成左旋,其他步骤一样。
    • N为P的右节点,X为N的左节点
      对应N为P的左节点,X为N的右节点,把右旋换成左旋,其他步骤一样。

红黑树的删除

跟插入一样,红黑树的删除就比二叉树多了一个步骤:删除完成之后重新调整树,让它再次符合二叉树的定义。那么二叉树是如何进行删除的呢?下面是出二叉树的删除步骤:

  1. 定位到二叉树需要删除的节点
  2. 如果节点没有支树了,直接删除节点
  3. 如果节点只有一个子支树,直接把这个子支树跟需要删除的节点的父节点相连
  4. 如果节点有两个支树,找到节点的左支树的最大值,或者右支树的最小值,然后把这个值(这里我们使用左支树的最大值)赋值给需要删除的节点(注意:当前需要被删除的节点不会被删掉了,只是被赋予了新值)。然后我们再删除掉拥有左支树的最大值的这个节点,因为这个节点是左边最大的了,那么它的右分支肯定为空,也就是说最多只可能有一个支树。这时候我们可以转换为情况2,3处理了。

需要理解的是,我们常说的删除一颗树中的一个节点,表示只要这颗树中不存在这个节点所存储的值,就表明删除了,并不一定要真正的抹掉这个节点!

下面争对上面的第四种举一个例子:

8d283f3e10b525206c7074baff599c28.png

1f92baf13add07f48203f9aca8295da6.png

经过上面的介绍,相信大家对二叉树的删除都有一点了解。红黑树的删除也会经过上面的步骤,不同的是,删除之后会调整红黑树,让它重新符合红黑树的特征,下面分析删除完成之后的情况。

这里假设一个节点P,删除了它的一个子节点X,删除完成之后,N节点顶替掉了X节点,成为P的子节点。X节点的兄弟节点为S,S的左右节点为SL,SR。

对于上述举出的删除的例子来说,这个P节点就是10,X节点就是11,N节点是NULL,S节点为13,SL为NULL,SR为NULL。注意,虽然上述删除的是12,但是我们讨论的X确并不是12。这是因为我们把删除节点12这个问题转换为了删除节点11。最终树中确实少了12,但是它的值只是被新值覆盖,节点被没有删除。而我们讨论的是被抹掉的节点。

所以有了上面的前提,我们分析问题的切入点就只有两个,被删除的节点有0个支树,被删除的的节点有1个支树。为什么没有被删除的节点有两个支树的情况呢?因为如果有两个,通过上面介绍的二叉树的删除步骤4,我们可以把删除具有两个子树的节点的情况转变为删除具有一个子树或者具有0颗子树的情况。

比如对于上面的例子,我们把删除12(这个节点有两个子树),转变为删除11(具有0颗子树)的情况。下面从被删除节点的支树数量的个数开始分析:

  1. 被删除的X节点没有分支
    这种情况直接删除节点就完事了,不需要做任何调整。我们常说调整红黑树,是因为不符合红黑树的特征4和特征5。而删除一个没有分支的节点,用屁股随便想一下就知道不会影响这两条。
  2. 被删除的X节点有一个分支这个时候的切入点就是被删除节点的颜色
  • 被删除的X节点为红色
    这种也是我们最喜欢的情况,不需要调整啊。因为根据红黑树的特征4,此时X的父节点P必为黑色,所以N节点为红还是黑都不会影响特征4。而X节点又为红,也就是说,经过原来X的那条支路只是少了一个红节点,也不会影响特征5。
  • 被删除的节点是黑色
    这又要分为很多种情况了,这里你可以一条一条分析N,U和P节点的颜色。我们分析的切入点是,顶替X节点的N节点的颜色。
说明:红底的代表红节点,黑底的代表黑节点,白底的代表颜色未知的节点(即可黑可红)。
    • (1) N节点为红
      这种情况也很简单,直接把N染成黑色

0d9e49884f468f8decef168389115274.png

经过上面的操作,不仅N不会和P产生两个连续红节点的情况,而且经过N节点的路径多了一个黑节点,正好替代被删除的黑节点X。
(2) N节点为黑
然后从S节点的颜色开始分析
S节点为红色
因为S为红色,根据红黑树的定义,P,SL,SR肯定都是黑色,如下图:

6c2799f31982e7e0649415e67459a346.png


这里我们把P节点进行左旋,然后交换S和P节点的颜色

eb25a6269c8c8bad0fb2d8f015bd9d9c.png


上面的S节点为P节点的右节点,所以左旋P节点。如果S节点为P节点的左节点,则是右旋P节点。
分析N,SL,SR节点在操作前和操作后的路径经过的黑色节点数量,发现都没有改变。但是我们把N节点的兄弟节点为红色的转换为黑色的情况。为黑色的处理情况如下处理。
S节点为黑色,SR节点为红的情况。如下图所示:

e284d862649d89dceecfd4f698fb0abb.png


由于原来P的左边有一个黑节点X,现在被删除了,所以左边路径上少了一个黑节点。
这种情况,直接左旋P节点,然后交换P,S的颜色,并且把SR换成黑色。

6bf29b0b4460681728858f60d15a6a25.png

533c272fdf0203ec986b1165ebe628b3.png

上面的S节点为P节点的右节点,所以取了SR来举例子。如果S为P节点的左节点,那么就应该去SL。然后把左旋改成右旋。
通过如上操作,不难发现,经过N节点的路径的黑色节点数量多了一个,而经过SL,SR节点的路径的黑色节点并没有变少,符合红黑树定义4,5。
S节点为黑色,SR节点为黑
这种情况下只剩下P节点和SL节点的颜色没有确定,这两个节点的组合,总共可能有四种情况:

  1. P为黑,SL为黑色
  2. P为白,SL为黑
  3. P为黑,SL为白
  4. P为白,SL为白

b41bd66d5ae33af667ebda7688f1559e.png
    • 对于第一种和第二种情况,都是转换成如下的情况:

4cd44d57a60ab080a612c3dad8ca319a.png

第一种是直接把S变成红色。然后P的左右支树的节点数量一样(P的左子树少了个黑色节点X)。
但是对于经过P节点的支路上来说,全部都少了一个黑节点(比如经过SL和SR节点的支路)。
所以可以把P节点看成N节点,进行递归,分析P节点的父节点,兄弟节点。如果中途一直没有解决,则会递归到根节点。如果根节点此时为红色,则变回黑色。、第二种直接把P和S节点的颜色对调,就不需要再调整了。因为对于经过N节点的支路多了一个黑节点,对于SL,SR来说没有变,正好符合情况。第三种第四种其实都是对S节点进行右旋,然后交换SL和S的颜色最后转化成S节点为黑色,SR节点为红的情况。

8f520133d406a7968d2250151867c972.png

红黑树的代码实现

引用linux红黑树的经典实现

红黑树的实现文件(rbtree.h)

/*Red Black Trees(C) 1999  Andrea Arcangeli <andrea@suse.de>This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2 of the License, or(at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USAlinux/include/linux/rbtree.hTo use rbtrees you'll have to implement your own insert and search cores.This will avoid us to use callbacks and to drop drammatically performances.I know it's not the cleaner way,  but in C (not in C++) to getperformances and genericity...Some example of insert and search follows here. The search is a plainnormal search over an ordered tree. The insert instead must be implementedin two steps: First, the code must insert the element in order as a red leafin the tree, and then the support library function rb_insert_color() mustbe called. Such function will do the not trivial work to rebalance therbtree, if necessary.-----------------------------------------------------------------------
static inline struct page * rb_search_page_cache(struct inode * inode,unsigned long offset)
{struct rb_node * n = inode->i_rb_page_cache.rb_node;struct page * page;while (n){page = rb_entry(n, struct page, rb_page_cache);if (offset < page->offset)n = n->rb_left;else if (offset > page->offset)n = n->rb_right;elsereturn page;}return NULL;
}static inline struct page * __rb_insert_page_cache(struct inode * inode,unsigned long offset,struct rb_node * node)
{struct rb_node ** p = &inode->i_rb_page_cache.rb_node;struct rb_node * parent = NULL;struct page * page;while (*p){parent = *p;page = rb_entry(parent, struct page, rb_page_cache);if (offset < page->offset)p = &(*p)->rb_left;else if (offset > page->offset)p = &(*p)->rb_right;elsereturn page;}rb_link_node(node, parent, p);return NULL;
}static inline struct page * rb_insert_page_cache(struct inode * inode,unsigned long offset,struct rb_node * node)
{struct page * ret;if ((ret = __rb_insert_page_cache(inode, offset, node)))goto out;rb_insert_color(node, &inode->i_rb_page_cache);out:return ret;
}
-----------------------------------------------------------------------
*/#ifndef    _SLINUX_RBTREE_H
#define    _SLINUX_RBTREE_H#include <stdio.h>
//#include <linux/kernel.h>
//#include <linux/stddef.h>struct rb_node
{unsigned long  rb_parent_color;
#define    RB_RED        0
#define    RB_BLACK    1struct rb_node *rb_right;struct rb_node *rb_left;
} /*  __attribute__((aligned(sizeof(long))))*/;/* The alignment might seem pointless, but allegedly CRIS needs it */struct rb_root
{struct rb_node *rb_node;
};#define rb_parent(r)   ((struct rb_node *)((r)->rb_parent_color & ~3))
#define rb_color(r)   ((r)->rb_parent_color & 1)
#define rb_is_red(r)   (!rb_color(r))
#define rb_is_black(r) rb_color(r)
#define rb_set_red(r)  do { (r)->rb_parent_color &= ~1; } while (0)
#define rb_set_black(r)  do { (r)->rb_parent_color |= 1; } while (0)static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p)
{rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p;
}
static inline void rb_set_color(struct rb_node *rb, int color)
{rb->rb_parent_color = (rb->rb_parent_color & ~1) | color;
}#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)#define container_of(ptr, type, member) ({          const typeof( ((type *)0)->member ) *__mptr = (ptr);    (type *)( (char *)__mptr - offsetof(type,member) );})#define RB_ROOT    (struct rb_root) { NULL, }
#define    rb_entry(ptr, type, member) container_of(ptr, type, member)#define RB_EMPTY_ROOT(root)    ((root)->rb_node == NULL)
#define RB_EMPTY_NODE(node)    (rb_parent(node) == node)
#define RB_CLEAR_NODE(node)    (rb_set_parent(node, node))static inline void rb_init_node(struct rb_node *rb)
{rb->rb_parent_color = 0;rb->rb_right = NULL;rb->rb_left = NULL;RB_CLEAR_NODE(rb);
}extern void rb_insert_color(struct rb_node *, struct rb_root *);
extern void rb_erase(struct rb_node *, struct rb_root *);typedef void (*rb_augment_f)(struct rb_node *node, void *data);extern void rb_augment_insert(struct rb_node *node,rb_augment_f func, void *data);
extern struct rb_node *rb_augment_erase_begin(struct rb_node *node);
extern void rb_augment_erase_end(struct rb_node *node,rb_augment_f func, void *data);/* Find logical next and previous nodes in a tree */
extern struct rb_node *rb_next(const struct rb_node *);
extern struct rb_node *rb_prev(const struct rb_node *);
extern struct rb_node *rb_first(const struct rb_root *);
extern struct rb_node *rb_last(const struct rb_root *);/* Fast replacement of a single node without remove/rebalance/add/rebalance */
extern void rb_replace_node(struct rb_node *victim, struct rb_node *new,struct rb_root *root);static inline void rb_link_node(struct rb_node * node, struct rb_node * parent,struct rb_node ** rb_link)
{node->rb_parent_color = (unsigned long )parent;node->rb_left = node->rb_right = NULL;*rb_link = node;
}#endif    /* _LINUX_RBTREE_H */

红黑树的实现文件(rbtree.c)

/*Red Black Trees(C) 1999  Andrea Arcangeli <andrea@suse.de>(C) 2002  David Woodhouse <dwmw2@infradead.org>This program is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2 of the License, or(at your option) any later version.This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with this program; if not, write to the Free SoftwareFoundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USAlinux/lib/rbtree.c
*/#include "rbtree.h"static void __rb_rotate_left(struct rb_node *node, struct rb_root *root)
{struct rb_node *right = node->rb_right;struct rb_node *parent = rb_parent(node);if ((node->rb_right = right->rb_left))rb_set_parent(right->rb_left, node);right->rb_left = node;rb_set_parent(right, parent);if (parent){if (node == parent->rb_left)parent->rb_left = right;elseparent->rb_right = right;}elseroot->rb_node = right;rb_set_parent(node, right);
}static void __rb_rotate_right(struct rb_node *node, struct rb_root *root)
{struct rb_node *left = node->rb_left;struct rb_node *parent = rb_parent(node);if ((node->rb_left = left->rb_right))rb_set_parent(left->rb_right, node);left->rb_right = node;rb_set_parent(left, parent);if (parent){if (node == parent->rb_right)parent->rb_right = left;elseparent->rb_left = left;}elseroot->rb_node = left;rb_set_parent(node, left);
}void rb_insert_color(struct rb_node *node, struct rb_root *root)
{struct rb_node *parent, *gparent;while ((parent = rb_parent(node)) && rb_is_red(parent)){gparent = rb_parent(parent);if (parent == gparent->rb_left){{register struct rb_node *uncle = gparent->rb_right;if (uncle && rb_is_red(uncle)){rb_set_black(uncle);rb_set_black(parent);rb_set_red(gparent);node = gparent;continue;}}if (parent->rb_right == node){register struct rb_node *tmp;__rb_rotate_left(parent, root);tmp = parent;parent = node;node = tmp;}rb_set_black(parent);rb_set_red(gparent);__rb_rotate_right(gparent, root);} else {{register struct rb_node *uncle = gparent->rb_left;if (uncle && rb_is_red(uncle)){rb_set_black(uncle);rb_set_black(parent);rb_set_red(gparent);node = gparent;continue;}}if (parent->rb_left == node){register struct rb_node *tmp;__rb_rotate_right(parent, root);tmp = parent;parent = node;node = tmp;}rb_set_black(parent);rb_set_red(gparent);__rb_rotate_left(gparent, root);}}rb_set_black(root->rb_node);
}static void __rb_erase_color(struct rb_node *node, struct rb_node *parent,struct rb_root *root)
{struct rb_node *other;while ((!node || rb_is_black(node)) && node != root->rb_node){if (parent->rb_left == node){other = parent->rb_right;if (rb_is_red(other)){rb_set_black(other);rb_set_red(parent);__rb_rotate_left(parent, root);other = parent->rb_right;}if ((!other->rb_left || rb_is_black(other->rb_left)) &&(!other->rb_right || rb_is_black(other->rb_right))){rb_set_red(other);node = parent;parent = rb_parent(node);}else{if (!other->rb_right || rb_is_black(other->rb_right)){rb_set_black(other->rb_left);rb_set_red(other);__rb_rotate_right(other, root);other = parent->rb_right;}rb_set_color(other, rb_color(parent));rb_set_black(parent);rb_set_black(other->rb_right);__rb_rotate_left(parent, root);node = root->rb_node;break;}}else{other = parent->rb_left;if (rb_is_red(other)){rb_set_black(other);rb_set_red(parent);__rb_rotate_right(parent, root);other = parent->rb_left;}if ((!other->rb_left || rb_is_black(other->rb_left)) &&(!other->rb_right || rb_is_black(other->rb_right))){rb_set_red(other);node = parent;parent = rb_parent(node);}else{if (!other->rb_left || rb_is_black(other->rb_left)){rb_set_black(other->rb_right);rb_set_red(other);__rb_rotate_left(other, root);other = parent->rb_left;}rb_set_color(other, rb_color(parent));rb_set_black(parent);rb_set_black(other->rb_left);__rb_rotate_right(parent, root);node = root->rb_node;break;}}}if (node)rb_set_black(node);
}void rb_erase(struct rb_node *node, struct rb_root *root)
{struct rb_node *child, *parent;int color;if (!node->rb_left)child = node->rb_right;else if (!node->rb_right)child = node->rb_left;else{struct rb_node *old = node, *left;node = node->rb_right;while ((left = node->rb_left) != NULL)node = left;if (rb_parent(old)) {if (rb_parent(old)->rb_left == old)rb_parent(old)->rb_left = node;elserb_parent(old)->rb_right = node;} elseroot->rb_node = node;child = node->rb_right;parent = rb_parent(node);color = rb_color(node);if (parent == old) {parent = node;} else {if (child)rb_set_parent(child, parent);parent->rb_left = child;node->rb_right = old->rb_right;rb_set_parent(old->rb_right, node);}node->rb_parent_color = old->rb_parent_color;node->rb_left = old->rb_left;rb_set_parent(old->rb_left, node);goto color;}parent = rb_parent(node);color = rb_color(node);if (child)rb_set_parent(child, parent);if (parent){if (parent->rb_left == node)parent->rb_left = child;elseparent->rb_right = child;}elseroot->rb_node = child;color:if (color == RB_BLACK)__rb_erase_color(child, parent, root);
}static void rb_augment_path(struct rb_node *node, rb_augment_f func, void *data)
{struct rb_node *parent;up:func(node, data);parent = rb_parent(node);if (!parent)return;if (node == parent->rb_left && parent->rb_right)func(parent->rb_right, data);else if (parent->rb_left)func(parent->rb_left, data);node = parent;goto up;
}/** after inserting @node into the tree, update the tree to account for* both the new entry and any damage done by rebalance*/
void rb_augment_insert(struct rb_node *node, rb_augment_f func, void *data)
{if (node->rb_left)node = node->rb_left;else if (node->rb_right)node = node->rb_right;rb_augment_path(node, func, data);
}/** before removing the node, find the deepest node on the rebalance path* that will still be there after @node gets removed*/
struct rb_node *rb_augment_erase_begin(struct rb_node *node)
{struct rb_node *deepest;if (!node->rb_right && !node->rb_left)deepest = rb_parent(node);else if (!node->rb_right)deepest = node->rb_left;else if (!node->rb_left)deepest = node->rb_right;else {deepest = rb_next(node);if (deepest->rb_right)deepest = deepest->rb_right;else if (rb_parent(deepest) != node)deepest = rb_parent(deepest);}return deepest;
}/** after removal, update the tree to account for the removed entry* and any rebalance damage.*/
void rb_augment_erase_end(struct rb_node *node, rb_augment_f func, void *data)
{if (node)rb_augment_path(node, func, data);
}/** This function returns the first node (in sort order) of the tree.*/
struct rb_node *rb_first(const struct rb_root *root)
{struct rb_node    *n;n = root->rb_node;if (!n)return NULL;while (n->rb_left)n = n->rb_left;return n;
}struct rb_node *rb_last(const struct rb_root *root)
{struct rb_node    *n;n = root->rb_node;if (!n)return NULL;while (n->rb_right)n = n->rb_right;return n;
}struct rb_node *rb_next(const struct rb_node *node)
{struct rb_node *parent;if (rb_parent(node) == node)return NULL;/* If we have a right-hand child, go down and then left as faras we can. */if (node->rb_right) {node = node->rb_right;while (node->rb_left)node=node->rb_left;return (struct rb_node *)node;}/* No right-hand children.  Everything down and left issmaller than us, so any 'next' node must be in the generaldirection of our parent. Go up the tree; any time theancestor is a right-hand child of its parent, keep goingup. First time it's a left-hand child of its parent, saidparent is our 'next' node. */while ((parent = rb_parent(node)) && node == parent->rb_right)node = parent;return parent;
}struct rb_node *rb_prev(const struct rb_node *node)
{struct rb_node *parent;if (rb_parent(node) == node)return NULL;/* If we have a left-hand child, go down and then right as faras we can. */if (node->rb_left) {node = node->rb_left;while (node->rb_right)node=node->rb_right;return (struct rb_node *)node;}/* No left-hand children. Go up till we find an ancestor whichis a right-hand child of its parent */while ((parent = rb_parent(node)) && node == parent->rb_left)node = parent;return parent;
}void rb_replace_node(struct rb_node *victim, struct rb_node *new,struct rb_root *root)
{struct rb_node *parent = rb_parent(victim);/* Set the surrounding nodes to point to the replacement */if (parent) {if (victim == parent->rb_left)parent->rb_left = new;elseparent->rb_right = new;} else {root->rb_node = new;}if (victim->rb_left)rb_set_parent(victim->rb_left, new);if (victim->rb_right)rb_set_parent(victim->rb_right, new);/* Copy the pointers/colour from the victim to the replacement */*new = *victim;
}

红黑树的测试文件(test.c)

/*** 根据Linux Kernel定义的红黑树(Red Black Tree)**/#include <stdio.h>
#include <stdlib.h>
#include "rbtree.h"#define CHECK_INSERT 0    // "插入"动作的检测开关(0,关闭;1,打开)
#define CHECK_DELETE 0    // "删除"动作的检测开关(0,关闭;1,打开)
#define LENGTH(a) ( (sizeof(a)) / (sizeof(a[0])) )typedef int Type;struct my_node {struct rb_node rb_node;    // 红黑树节点Type key;                // 键值// ... 用户自定义的数据
};/** 查找"红黑树"中键值为key的节点。没找到的话,返回NULL。*/
struct my_node *my_search(struct rb_root *root, Type key)
{struct rb_node *rbnode = root->rb_node;while (rbnode!=NULL){struct my_node *mynode = container_of(rbnode, struct my_node, rb_node);if (key < mynode->key)rbnode = rbnode->rb_left;else if (key > mynode->key)rbnode = rbnode->rb_right;elsereturn mynode;}return NULL;
}/** 将key插入到红黑树中。插入成功,返回0;失败返回-1。*/
int my_insert(struct rb_root *root, Type key)
{struct my_node *mynode; // 新建结点struct rb_node **tmp = &(root->rb_node), *parent = NULL;/* Figure out where to put new node */while (*tmp){struct my_node *my = container_of(*tmp, struct my_node, rb_node);parent = *tmp;if (key < my->key)tmp = &((*tmp)->rb_left);else if (key > my->key)tmp = &((*tmp)->rb_right);elsereturn -1;}// 如果新建结点失败,则返回。if ((mynode=malloc(sizeof(struct my_node))) == NULL)return -1;mynode->key = key;/* Add new node and rebalance tree. */rb_link_node(&mynode->rb_node, parent, tmp);rb_insert_color(&mynode->rb_node, root);return 0;
}/** 删除键值为key的结点*/
void my_delete(struct rb_root *root, Type key)
{struct my_node *mynode;// 在红黑树中查找key对应的节点mynodeif ((mynode = my_search(root, key)) == NULL)return ;// 从红黑树中删除节点mynoderb_erase(&mynode->rb_node, root);free(mynode);
}/** 打印"红黑树"*/
static void print_rbtree(struct rb_node *tree, Type key, int direction)
{if(tree != NULL){if(direction==0)    // tree是根节点printf("%2d(B) is rootn", key);else                // tree是分支节点printf("%2d(%s) is %2d's %6s childn", key, rb_is_black(tree)?"B":"R", key, direction==1?"right" : "left");if (tree->rb_left)print_rbtree(tree->rb_left, rb_entry(tree->rb_left, struct my_node, rb_node)->key, -1);if (tree->rb_right)print_rbtree(tree->rb_right,rb_entry(tree->rb_right, struct my_node, rb_node)->key,  1);}
}void my_print(struct rb_root *root)
{if (root!=NULL && root->rb_node!=NULL)print_rbtree(root->rb_node, rb_entry(root->rb_node, struct my_node, rb_node)->key,  0);
}void main()
{int a[] = {10, 40, 30, 60, 90, 70, 20, 50, 80};int i, ilen=LENGTH(a);struct rb_root mytree = RB_ROOT;printf("== 原始数据: ");for(i=0; i<ilen; i++)printf("%d ", a[i]);printf("n");for (i=0; i < ilen; i++){my_insert(&mytree, a[i]);
#if CHECK_INSERTprintf("== 添加节点: %dn", a[i]);printf("== 树的详细信息: n");my_print(&mytree);printf("n");
#endif}#if CHECK_DELETEfor (i=0; i<ilen; i++) {my_delete(&mytree, a[i]);printf("== 删除节点: %dn", a[i]);printf("== 树的详细信息: n");my_print(&mytree);printf("n");}
#endif
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/574909.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux中按行读取文件,Linux按行读取文件内容

方法1&#xff1a;while循环中执行效率最高&#xff0c;最常用的方法。function while_read_LINE_bottm(){While read LINEdoecho $LINEdone < $FILENAME}#!/bin/bashwhile read linedoecho $linedone < filename(待读取的文件)注释&#xff1a;习惯把这种方式叫做read釜…

酱油和gbt酱油哪个好_酱油越贵越好?认准瓶身这4处,轻松挑到好酱油!

买酱油这件事&#xff0c;爷叔阿姨们都不陌生。现在市面上酱油品种很多&#xff0c;除了普通的生抽、老抽以外&#xff0c;还有些别的口味&#xff0c;比如海鲜酱油、菌菇酱油等等&#xff0c;价格比普通的还高出不少&#xff0c;这些酱油真的更鲜更好吗&#xff1f;买酱油的2大…

linux网站465端口是什么端口,发送端口25,465,587端口疑问解答

25端口(SMTP)&#xff1a;25端口为SMTP(Simple Mail Transfer Protocol&#xff0c;简单邮件传输协议)服务所开放的&#xff0c;是用于发送邮件。如今绝大多数邮件服务器都使用该协议。当你给别人发送邮件时&#xff0c;你的机器的某个动态端口(大于1024)就会与邮件服务器的25号…

uml 时序图_程序猿都应学习的语言:看 25 张图学 UML

作者 | 逸珺责编 | 屠敏来源 | 嵌入式客栈作为程序猿都最好掌握的一门语言&#xff0c;那就是UML(Unified Modeling Language)&#xff0c;统一建模语言(UML)是软件工程领域中一种通用的开发建模语言&#xff0c;旨在提供一种可视化系统设计的标准方法。是开发人员、系统设计人…

新服务器的配置

新服务器的安装好那三款软件后还是打不开会报错&#xff0c;去服务器管理网站增加执行权即可。 转载于:https://www.cnblogs.com/longhun/p/4266665.html

Linux下好用的日志库,我使用過的Linux命令之tailf - 跟蹤日志文件/更好的tail -f版本...

用途說明tailf命令幾乎等同於tail -f&#xff0c;嚴格說來應該與tail --followname更相似些。當文件改名之后它也能繼續跟蹤&#xff0c;特別適合於日志文件的跟蹤(follow the growth of a log file)。與tail -f不同的是&#xff0c;如果文件不增長&#xff0c;它不會去訪問磁盤…

Android学习笔记(四十):Preference的使用

Preference直译为偏好&#xff0c;博友建议翻译为首选项。一些配置数据&#xff0c;一些我们上次点击选择的内容&#xff0c;我们希望在下次应用调起的时候依旧有效&#xff0c;无须用户再一次进行配置或选择。Android提供preference这个键值对的方式来处理这样的情况&#xff…

telegr怎么连接不上_无线网密码正确但是手机连接不上wifi?

现在企业、家庭、餐厅、宾馆到处都有WiFi无线网络信号&#xff0c;为大家的电脑、手机提供免费的无线网络服务&#xff0c;但是有的时候明明有信号而无线网密码正确却连不上&#xff0c;是多么痛苦的一件事情。那么无线网密码正确但是手机连接不上wifi怎么回事&#xff1f;下面…

hdu3652(数位dp)

要求找出范围内含有“13”且能被13整除的数字的个数 可以使用数位dp dp[i][j][0] 表示长度为i&#xff0c;余数为j&#xff0c;不含13的数字的个数 dp[i][j][1] 表示长度为i&#xff0c;余数为j&#xff0c;3开头的数字的个数 dp[i][j][2] 表示长度为i&#xff0c;余数为j&…

怎么查看电脑是不是禁ping_怎么查看电脑内存的大小,找回丢失的内存,电脑小技巧...

小A自己组装了一台电脑&#xff0c;配件都是在jd买了。他高高兴兴的安装了系统准备开始体验一把&#xff0c;同事突然问&#xff0c;你的电脑内存是多少&#xff0c;他点来点去也没找到答案。他只知道他买了一个8G的内存条。.我们一般查看内存有两个地方。一我们的电脑 属性 安…

finereport字段显示设置_QA | 表单如何设置字段显示逻辑?

Hi&#xff0c;大家好&#xff01;表姐又来给大家答疑解惑啦&#xff01;这一期的Q&A我们来聊聊表单如何设置字段显示逻辑。字段显示逻辑是什么&#xff1f;字段显示逻辑是指根据设定字段条件&#xff0c;显示或隐藏特定的字段&#xff0c;可以实现选择不同内容&#xff0c…

数据库管理之数据表管理(1)

数据类型 创建数据表 语法 CREATE TABLE 表名 ( 字段名1 数据类型 列的属性 列约束, 字段名2 数据类型 列的属性 列约束, 字段名3 数据类型 列的属性 列约束, … )列属性的格式&#xff1a; [NULL|NOT NULL] [IDENTITY(标识种子, 标识增量)]列约束的格式&#xff1a; [CONST…

AngularJs-指令和指令之间的交互(动感超人)

前言&#xff1a; 上节我们学习到了指令和控制器之间的交互&#xff0c;通过给指令添加动作&#xff0c;调用了控制器中的方法。本节我们学习指令和指令之间是如何交互的&#xff0c;我们通过一个小游戏来和大家一起学习&#xff0c;听大漠老师说这是国外的人写的demo&#xff…

vba 指定列后插入列_在不同的列左侧插入指定数量的空白列

任务: 在不同的列左侧插入指定数量的空白列(本例是要求在每列左侧插入第2行数字对应的空白列) 插入空白列前 插入空白列后 vba代码如下&#xff1a; Sub 在不同的列左侧插入指定数量的空白列() Dim m, n, i As Integer For n 5 To 1 Step -1 Sheets("Sheet1").C…

怎么监听linux防火墙,linux怎么查看防火墙是否开启并清除防火墙规则?

iptables是linux下的防火墙组件服务&#xff0c;相对于windows防火墙而言拥有更加强大的功能&#xff0c;下面我们就来看看linux系统中关于iptables的一般常见操作&#xff0c;判断linux系统是否启用了iptables服务&#xff0c;并清除防火墙规则的教程。一、检查iptables是否安…

手册如何看运放的db_如何处理金属离子与配体之间的配位键?

更多资讯&#xff0c;请访问www.yinfotek.com 或关注微信公众号“殷赋科技”。殷赋学术交流2群已建立&#xff0c;需求加群的朋友&#xff0c;请在公众号输入“加群”&#xff0c;验证后即入群。1A:请教大家一个问题&#xff1a;在做对接模拟的过程中蛋白活性中心存在重要金属离…

secure连不上远程地址_[笔记]Mariadb安装并配置远程访问

引言熟悉的小伙伴都知道我自用有一台NAS&#xff0c;并为此折腾了不少内容&#xff1a;西蒙宫&#xff1a;折腾&#xff0c;用J3455搭建家庭nas西蒙宫&#xff1a;让家庭NAS走向世界&#xff0c;兼谈Zerotier西蒙宫&#xff1a;折腾——组装NAS编译安装ffmpeg在NAS上部署了Next…

从Wireshark看TCP连接的建立与关闭

TCP是一种面向连接、可靠的协议。TCP连接的建立与断开&#xff0c;都是需要经过通信双方的协商。用一句话概括就是&#xff1a;三次握手say hello&#xff08;建立连接&#xff09;&#xff1b;四次握手say goodbye&#xff08;断开连接&#xff09;。要了解TCP连接的建立与断开…

用Visual C#来清空回收站(2)

四.程序的源代码&#xff08;recycled.cs&#xff09;、编译方法及运行后的界面&#xff1a;  &#xff08;1&#xff09;.程序的源代码&#xff1a;recycled.cs&#xff1a; using System.IO ; using System.Windows.Forms ; //导入程序中用到的名称空间 class Recycle { pu…

jq苹果手机全屏下点击无效果_苹果系统自带外挂?无需越狱也可录制动作脚本...

网络游戏开始流行起来之后&#xff0c;随之而来的是外挂的兴起&#xff0c;各式各样的外挂中有两种最为致命&#xff1a;物理外挂和系统自带外挂。随着国内电竞行业和电竞直播的兴起&#xff0c;“物理外挂”出现在我们周围的频率也越来越高&#xff0c;比如扩大视角的显示器、…