红黑树和平衡二叉树的区别_面试题精选红黑树(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下软件(库)的更新命令详解

在ubuntu服务器下安装包的时候&#xff0c;经常会用到sudo apt-get install 包名 或 sudo pip install 包名&#xff0c;那么两者有什么区别呢&#xff1f;1.区别pip用来安装来自PyPI(https://www.python.org/)的python所有的依赖包&#xff0c;并且可以选择安装任何在PyPI上已…

查看oracle死锁进程并结束死锁

查看oracle死锁进程并结束死锁 摘自: http://sqcjy111.iteye.com/blog/1183928 查看锁表进程SQL语句1&#xff1a; select sess.sid, sess.serial#, lo.oracle_username, lo.os_user_name, ao.object_name, lo.locked_mode from v$locked_object lo, dba_objects ao, v$sessio…

python科学坐标图绘制的四个要素_Python3.0科学计算学习之绘图(四)

绘制三维图&#xff1a;mplot3d工具包提供了点、线、等值线、曲面和所有其他基本组件以及三维旋转缩放的三维绘图。1.散点的三维数据图from mpl_toolkits.mplot3d import axes3d #需要从mplot3d模块中导入axes 3D类型import numpy as npimport matplotlib.p…

linux shell tr命令,linux shell tr命令详解

tr命令是linux下的一个命令&#xff0c;那么它的语法和用途是什么呢?下面由学习啦小编为大家整理了linux shell tr命令的相关知识&#xff0c;希望大家喜欢!linux shell tr命令tr是translate的简写&#xff0c;亦即翻译&#xff0c;但是遗憾的是&#xff0c;它不能翻译句子&am…

PHP 函数:intval()

intval 变量转成整数类型。 语法: int intval(mixed var, int [base]); 返回值: 整数 函数种类: PHP 系统功能 内容说明:本函数可将变量转成整数类型。可省略的参数 base 是转换的基底&#xff0c;默认值为 10。转换的变量 var 可以为数组或类之外的任何类型变量。转载于:htt…

python桌面应用html_是否将Python后端与HTML / CSS / JS用户界面集成到桌面应用程序? - javascript...

在创建桌面应用程序时&#xff0c;如何或将Python代码与HTML / CSS / JS集成为用户界面&#xff0c;该如何做&#xff1f;一个简单的例子&#xff1b;如果我想用Python创建一个打印“ Hello World&#xff01;”的函数并使用HTML / CSS / JS创建用户界面(使其精美&#xff0c;也…

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

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

Visual C++ 基础数据类型的转换

16.1如何将基本数据类型转换成CString类型 用CString的Format方法 void CDemoView::OnDraw(CDC* pDC) {int a 100;double b 1.23;//将整型转换成CStringCString str1 _T("");str1.Format(_T("%d"), a);//将实型转换成CStringCString str2 _T("&qu…

python 片段_python片段程序

from PIL import Imageimport re#给图片添加一层蒙版&#xff0c;可以设置位置&#xff0c;尺寸&#xff0c;颜色&#xff0c;透明度def img_add_glass(img,new_imgglass,glass_factor0.3,glass_color#ffffff,glass_x0,glass_y0,glass_width300,glass_height100):imageobjectif…

linux ruby 安装路径,Linux(CentOS 7)安装ruby

首先下载Linux版ruby源码“ruby-2.7.0.tar.gz”&#xff0c;上传并解压到/usr/local路径下&#xff1a;tar -xzvf ruby-2.7.0.tar.gz安装编译依赖工具&#xff0c;gcc、gcc-c、gdbm-devel、readline-devel和openssl-devel&#xff1a;yum install gccyum install gcc-cyum inst…

开心一笑

1转载于:https://www.cnblogs.com/luquanmingren/p/4261912.html

酱油和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号…

分析DuxCms之AdminController

1 2 /**3 * 后台模板显示 调用内置的模板引擎显示方法&#xff0c;4 * access protected5 * param string $templateFile 指定要调用的模板文件6 * return void7 */8 protected function adminDisplay($templateFile) {9 //获取菜单 10 …

在python语言中不能作为变量名的是什么_4、 在 Python 中可以使用 if 作为变量名。 (1.0分)_学小易找答案...

【判断题】2、 Python 变量使用前必须先声明 , 并且一旦声明就不能在当前作用域内改变其类型。 (1.0分)【填空题】Python语句.join(list(hello world!))执行的结果是____________________。 (1.0分)【填空题】Python语句list(range(1,10,3))执行结果为___________________。 (1…

hdu 3572 Task Schedule 网络流

题目链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid3572Our geometry princess XMM has stoped her study in computational geometry to concentrate on her newly opened factory. Her factory has introduced M new machines in order to process the coming …

linux格式化ext4分区工具,linux学习笔记-磁盘分区、格式化与挂载

磁盘分区、格式化与挂载磁盘分区、格式化与挂载一、给磁盘分区分区工具介绍fdisk:分区时只修改分区表信息&#xff1b;操作简单&#xff1b;不支持大于2T的分区&#xff1b;只能使用交互式来分区。parted:直接将分区信息写入磁盘&#xff1b;操作比较复杂&#xff1b;支持大于2…

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

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

was6 linux 卸载,重新安装was61

昨天折腾一天&#xff0c;或者是少安装了compat-libstdc-33包导致app2出不来安装向导&#xff0c;或者是修改了主机名导致./addNode.sh xxx执行异常&#xff0c;诸多不爽后&#xff0c;决定从头再来&#xff01;手动删除/was下所有文件&#xff0c;导致再次安装时&#xff0c;报…

新服务器的配置

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