17张图带你解析红黑树的原理!保证你能看懂!

红黑树的实现原理和应用场景 红黑树(Red-Black Tree)是一种自平衡的二叉查找树,它通过特定的规则确保树在插入和删除操作后能保持相对平衡,从而保证查找、插入、删除等操作的时间复杂度为O(log n)。红黑树是一种高效的自平衡二叉查找树,通过精心设计的平衡规则和旋转操作,在各种动态数据管理场景中表现出色。虽然实现相对复杂,但其优秀的综合性能使其成为许多高级数据结构和系统组件的首选实现方式。理解红黑树的原理和实现,对于深入掌握计算机科学中的数据结构和算法设计具有重要意义。 阅读详情

二叉查找树

由于红黑树本质上就是一棵二叉查找树,所以在了解红黑树之前,咱们先来看下二叉查找树。

二叉查找树(Binary Search Tree),也称有序二叉树(ordered binary tree),排序二叉树(sorted binary tree),是指一棵空树或者具有下列性质的二叉树:

  • 若任意结点的左子树不空,则左子树上所有结点的值均小于它的根结点的值;

  • 若任意结点的右子树不空,则右子树上所有结点的值均大于它的根结点的值;

  • 任意结点的左、右子树也分别为二叉查找树。

  • 没有键值相等的结点(no duplicate nodes)。

因为,一棵由n个结点,随机构造的二叉查找树的高度为lgn,所以顺理成章,一般操作的执行时间为O(lgn).(至于n个结点的二叉树高度为lgn的证明,可参考算法导论 第12章 二叉查找树 第12.4节)。

但二叉树若退化成了一棵具有n个结点的线性链后,则此些操作最坏情况运行时间为O(n)。后面我们会看到一种基于二叉查找树-红黑树,它通过一些性质使得树相对平衡,使得最终查找、插入、删除的时间复杂度最坏情况下依然为O(lgn)。

红黑树

前面我们已经说过,红黑树,本质上来说就是一棵二叉查找树,但它在二叉查找树的基础上增加了着色和相关的性质使得红黑树相对平衡,从而保证了红黑树的查找、插入、删除的时间复杂度最坏为O(log n)。

但它是如何保证一棵n个结点的红黑树的高度始终保持在h = logn的呢?这就引出了红黑树的5条性质:

1)每个结点要么是红的,要么是黑的。
2)根结点是黑的。
3)每个叶结点(叶结点即指树尾端NIL指针或NULL结点)是黑的。
4)如果一个结点是红的,那么它的俩个儿子都是黑的。
5)对于任一结点而言,其到叶结点树尾端NIL指针的每一条路径都包含相同数目的黑结点。

正是红黑树的这5条性质,使得一棵n个结点是红黑树始终保持了logn的高度,从而也就解释了上面我们所说的“红黑树的查找、插入、删除的时间复杂度最坏为O(log n)”这一结论的原因。

如下图所示,即是一颗红黑树:

 

上文中我们所说的 "叶结点" 或"NULL结点",它不包含数据而只充当树在此结束的指示,这些结点以及它们的父结点,在绘图中都会经常被省略。

树的旋转知识

当我们在对红黑树进行插入和删除等操作时,对树做了修改,那么可能会违背红黑树的性质。

为了继续保持红黑树的性质,我们可以通过对结点进行重新着色,以及对树进行相关的旋转操作,即修改树中某些结点的颜色及指针结构,来达到对红黑树进行插入或删除结点等操作后,继续保持它的性质或平衡。

树的旋转,分为左旋和右旋,以下借助图来做形象的解释和介绍:

1.左旋

 

如上图所示:

当在某个结点pivot上,做左旋操作时,我们假设它的右孩子y不是NIL[T],pivot可以为任何不是NIL[T]的左孩子结点。

左旋以pivot到y之间的链为“支轴”进行,它使y成为该孩子树新的根,而y的左孩子b则成为pivot的右孩子。

左旋操作的参考代码如下所示(以x代替上述的pivot):

 LEFT-ROTATE(T, x)  
1  y ← right[x] ▹ Set y.  
2  right[x] ← left[y]      ▹ Turn y's left subtree into x's right subtree.  
3  p[left[y]] ← x  
4  p[y] ← p[x]             ▹ Link x's parent to y.  
5  if p[x] = nil[T]  
6     then root[T] ← y  
7     else if x = left[p[x]]  
8             then left[p[x]] ← y  
9             else right[p[x]] ← y  
10  left[y] ← x             ▹ Put x on y's left.  
11  p[x] ← y  

2.右旋

右旋与左旋差不多,再此不做详细介绍。

 

对于树的旋转,能保持不变的只有原树的搜索性质,而原树的红黑性质则不能保持,在红黑树的数据插入和删除后可利用旋转和颜色重涂来恢复树的红黑性质。

 

红黑树的插入

要真正理解红黑树的插入和删除,还得先理解二叉查找树的插入和删除。磨刀不误砍柴工,咱们再来分别了解下二叉查找树的插入和删除。

 

二叉查找树的插入

如果要在二叉查找树中插入一个结点,首先要查找到结点插入的位置,然后进行插入,假设插入的结点为z的话,插入的伪代码如下:

TREE-INSERT(T, z)
 1  y ← NIL
 2  x ← root[T]
 3  while x ≠ NIL
 4      do y ←  x
 5         if key[z] < key[x]
 6            then x ← left[x]
 7            else x ← right[x]
 8  p[z] ← y
 9  if y = NIL
10     then root[T] ← z              ⊹ Tree T was empty
11     else if key[z] < key[y]
12             then left[y] ← z
13             else right[y] ← z

可以看到,上述第3-7行代码即是在二叉查找树中查找z待插入的位置,如果插入结点z小于当前遍历到的结点,则到当前结点的左子树中继续查找,如果z大于当前结点,则到当前结点的右子树中继续查找,第9-13行代码找到待插入的位置,如果z依然比此刻遍历到的新的当前结点小,则z作为当前结点的左孩子,否则作为当前结点的右孩子。欢迎大家关注我的公种浩【程序员追风】,整理了2019年多家公司java面试题资料100多页pdf文档,文章都会在里面更新,整理的资料也会放在里面。

 

红黑树的插入和插入修复

现在我们了解了二叉查找树的插入,接下来,咱们便来具体了解红黑树的插入操作。红黑树的插入相当于在二叉查找树插入的基础上,为了重新恢复平衡,继续做了插入修复操作。

假设插入的结点为z,红黑树的插入伪代码具体如下所示:

RB-INSERT(T, z)  
 1  y ← nil[T]  
 2  x ← root[T]  
 3  while x ≠ nil[T]  
 4      do y ← x  
 5         if key[z] < key[x]  
 6            then x ← left[x]  
 7            else x ← right[x]  
 8  p[z] ← y  
 9  if y = nil[T]  
10     then root[T] ← z  
11     else if key[z] < key[y]  
12             then left[y] ← z  
13             else right[y] ← z  
14  left[z] ← nil[T]  
15  right[z] ← nil[T]  
16  color[z] ← RED  
17  RB-INSERT-FIXUP(T, z)  

我们把上面这段红黑树的插入代码,跟我们之前看到的二叉查找树的插入代码,可以看出,RB-INSERT(T, z)前面的第1-13行代码基本就是二叉查找树的插入代码,然后第14-16行代码把z的左孩子、右孩子都赋为叶结点nil,再把z结点着为红色,最后为保证红黑性质在插入操作后依然保持,调用一个辅助程序RB-INSERT-FIXUP来对结点进行重新着色,并旋转。

换言之

  • 如果插入的是根结点,因为原树是空树,此情况只会违反性质2,所以直接把此结点涂为黑色。

  • 如果插入的结点的父结点是黑色,由于此不会违反性质2和性质4,红黑树没有被破坏,所以此时也是什么也不做。

但当遇到下述3种情况时:

  • 插入修复情况1:如果当前结点的父结点是红色且祖父结点的另一个子结点(叔叔结点)是红色

  • 插入修复情况2:当前结点的父结点是红色,叔叔结点是黑色,当前结点是其父结点的右子

  • 插入修复情况3:当前结点的父结点是红色,叔叔结点是黑色,当前结点是其父结点的左子

又该如何调整呢?答案就是根据红黑树插入代码RB-INSERT(T, z)最后一行调用的RB-INSERT-FIXUP(T,z)所示操作进行,具体如下所示:

RB-INSERT-FIXUP(T,z)
 1 while color[p[z]] = RED  
 2     do if p[z] = left[p[p[z]]]  
 3           then y ← right[p[p[z]]]  
 4                if color[y] = RED  
 5                   then color[p[z]] ← BLACK                    ▹ Case 1  
 6                        color[y] ← BLACK                       ▹ Case 1  
 7                        color[p[p[z]]] ← RED                   ▹ Case 1  
 8                        z ← p[p[z]]                            ▹ Case 1  
 9                   else if z = right[p[z]]  
10                           then z ← p[z]                       ▹ Case 2  
11                                LEFT-ROTATE(T, z)              ▹ Case 2  
12                           color[p[z]] ← BLACK                 ▹ Case 3  
13                           color[p[p[z]]] ← RED                ▹ Case 3  
14                           RIGHT-ROTATE(T, p[p[z]])            ▹ Case 3  
15           else (same as then clause  
                         with "right" and "left" exchanged)  
16 color[root[T]] ← BLACK  

下面,咱们来分别处理上述3种插入修复情况。

插入修复情况1: 当前结点的父结点是红色且祖父结点的另一个子结点(叔叔结点)是红色。

即如下代码所示:

 1 while color[p[z]] = RED  
 2     do if p[z] = left[p[p[z]]]  
 3           then y ← right[p[p[z]]]  
 4                if color[y] = RED  

此时父结点的父结点一定存在,否则插入前就已不是红黑树。

与此同时,又分为父结点是祖父结点的左子还是右子,对于对称性,我们只要解开一个方向就可以了。

在此,我们只考虑父结点为祖父左子的情况。

同时,还可以分为当前结点是其父结点的左子还是右子,但是处理方式是一样的。我们将此归为同一类。

对策:将当前结点的父结点和叔叔结点涂黑,祖父结点涂红,把当前结点指向祖父结点,从新的当前结点重新开始算法。即如下代码所示:

 5                   then color[p[z]] ← BLACK                    ▹ Case 1  
 6                        color[y] ← BLACK                       ▹ Case 1  
 7                        color[p[p[z]]] ← RED                   ▹ Case 1  
 8                        z ← p[p[z]]                            ▹ Case 1  

针对情况1,变化前[当前结点为4结点]:

 

变化后:

 

插入修复情况2: 当前结点的父结点是红色,叔叔结点是黑色,当前结点是其父结点的右子

对策:当前结点的父结点做为新的当前结点,以新当前结点为支点左旋。即如下代码所示:

 9                   else if z = right[p[z]]
10                           then z ← p[z]                       ▹ Case 2
11                                LEFT-ROTATE(T, z)              ▹ Case 2

如下图所示,变化前[当前结点为7结点]:

 

变化后:

 

插入修复情况3: 当前结点的父结点是红色,叔叔结点是黑色,当前结点是其父结点的左子

解法:父结点变为黑色,祖父结点变为红色,在祖父结点为支点右旋,操作代码为:

12                           color[p[z]] ← BLACK                 ▹ Case 3
13                           color[p[p[z]]] ← RED                ▹ Case 3
14                           RIGHT-ROTATE(T, p[p[z]])            ▹ Case 3

最后,把根结点涂为黑色,整棵红黑树便重新恢复了平衡。

如下图所示[当前结点为2结点]

 

变化后:

 

 

红黑树的删除

ok,接下来,咱们最后来了解,红黑树的删除操作。

"我们删除的结点的方法与常规二叉搜索树中删除结点的方法是一样的,如果被删除的结点不是有双非空子女,则直接删除这个结点,用它的唯一子结点顶替它的位置,如果它的子结点都是空结点,那就用空结点顶替它的位置,如果它的双子全为非空,我们就把它的直接后继结点内容复制到它的位置,之后以同样的方式删除它的后继结点,它的后继结点不可能是双子非空,因此此传递过程最多只进行一次。”

 

二叉查找树的删除

继续讲解之前,补充说明下二叉树结点删除的几种情况,待删除的结点按照儿子的个数可以分为三种:

  1. 没有儿子,即为叶结点。直接把父结点的对应儿子指针设为NULL,删除儿子结点就OK了。

  2. 只有一个儿子。那么把父结点的相应儿子指针指向儿子的独生子,删除儿子结点也OK了。

  3. 有两个儿子。这是最麻烦的情况,因为你删除结点之后,还要保证满足搜索二叉树的结构。其实也比较容易,我们可以选择左儿子中的最大元素或者右儿子中的最小元素放到待删除结点的位置,就可以保证结构的不变。当然,你要记得调整子树,毕竟又出现了结点删除。习惯上大家选择左儿子中的最大元素,其实选择右儿子的最小元素也一样,没有任何差别,只是人们习惯从左向右。这里咱们也选择左儿子的最大元素,将它放到待删结点的位置。左儿子的最大元素其实很好找,只要顺着左儿子不断的去搜索右子树就可以了,直到找到一个没有右子树的结点。那就是最大的了。

二叉查找树的删除代码如下所示:

TREE-DELETE(T, z)
 1  if left[z] = NIL or right[z] = NIL
 2      then y ← z
 3      else y ← TREE-SUCCESSOR(z)
 4  if left[y] ≠ NIL
 5      then x ← left[y]
 6      else x ← right[y]
 7  if x ≠ NIL
 8      then p[x] ← p[y]
 9  if p[y] = NIL
10      then root[T] ← x
11      else if y = left[p[y]]
12              then left[p[y]] ← x
13              else right[p[y]] ← x
14  if y ≠ z
15      then key[z] ← key[y]
16           copy y's satellite data into z
17  return y

 

红黑树的删除和删除修复

OK,回到红黑树上来,红黑树结点删除的算法实现是:

RB-DELETE(T, z) 单纯删除结点的总操作

 1 if left[z] = nil[T] or right[z] = nil[T]  
 2    then y ← z  
 3    else y ← TREE-SUCCESSOR(z)  
 4 if left[y] ≠ nil[T]  
 5    then x ← left[y]  
 6    else x ← right[y]  
 7 p[x] ← p[y]  
 8 if p[y] = nil[T]  
 9    then root[T] ← x  
10    else if y = left[p[y]]  
11            then left[p[y]] ← x  
12            else right[p[y]] ← x  
13 if y ≠ z  
14    then key[z] ← key[y]  
15         copy y's satellite data into z  
16 if color[y] = BLACK  
17    then RB-DELETE-FIXUP(T, x)  
18 return y  

“在删除结点后,原红黑树的性质可能被改变,如果删除的是红色结点,那么原红黑树的性质依旧保持,此时不用做修正操作,如果删除的结点是黑色结点,原红黑树的性质可能会被改变,我们要对其做修正操作。那么哪些树的性质会发生变化呢,如果删除结点不是树唯一结点,那么删除结点的那一个支的到各叶结点的黑色结点数会发生变化,此时性质5被破坏。如果被删结点的唯一非空子结点是红色,而被删结点的父结点也是红色,那么性质4被破坏。如果被删结点是根结点,而它的唯一非空子结点是红色,则删除后新根结点将变成红色,违背性质2。”

RB-DELETE-FIXUP(T, x) 恢复与保持红黑性质的工作

 1 while x ≠ root[T] and color[x] = BLACK  
 2     do if x = left[p[x]]  
 3           then w ← right[p[x]]  
 4                if color[w] = RED  
 5                   then color[w] ← BLACK                        ▹  Case 1  
 6                        color[p[x]] ← RED                       ▹  Case 1  
 7                        LEFT-ROTATE(T, p[x])                    ▹  Case 1  
 8                        w ← right[p[x]]                         ▹  Case 1  
 9                if color[left[w]] = BLACK and color[right[w]] = BLACK  
10                   then color[w] ← RED                          ▹  Case 2  
11                        x ← p[x]                                ▹  Case 2  
12                   else if color[right[w]] = BLACK  
13                           then color[left[w]] ← BLACK          ▹  Case 3  
14                                color[w] ← RED                  ▹  Case 3  
15                                RIGHT-ROTATE(T, w)              ▹  Case 3  
16                                w ← right[p[x]]                 ▹  Case 3  
17                         color[w] ← color[p[x]]                 ▹  Case 4  
18                         color[p[x]] ← BLACK                    ▹  Case 4  
19                         color[right[w]] ← BLACK                ▹  Case 4  
20                         LEFT-ROTATE(T, p[x])                   ▹  Case 4  
21                         x ← root[T]                            ▹  Case 4  
22        else (same as then clause with "right" and "left" exchanged)  
23 color[x] ← BLACK  

“上面的修复情况看起来有些复杂,下面我们用一个分析技巧:我们从被删结点后来顶替它的那个结点开始调整,并认为它有额外的一重黑色。这里额外一重黑色是什么意思呢,我们不是把红黑树的结点加上除红与黑的另一种颜色,这里只是一种假设,我们认为我们当前指向它,因此空有额外一种黑色,可以认为它的黑色是从它的父结点被删除后继承给它的,它现在可以容纳两种颜色,如果它原来是红色,那么现在是红+黑,如果原来是黑色,那么它现在的颜色是黑+黑。有了这重额外的黑色,原红黑树性质5就能保持不变。现在只要恢复其它性质就可以了,做法还是尽量向根移动和穷举所有可能性。"--saturnman。

如果是以下情况,恢复比较简单:

  • a)当前结点是红+黑色

解法,直接把当前结点染成黑色,结束此时红黑树性质全部恢复。

  • b)当前结点是黑+黑且是根结点, 解法:什么都不做,结束。

但如果是以下情况呢?:

  • 删除修复情况1:当前结点是黑+黑且兄弟结点为红色(此时父结点和兄弟结点的子结点分为黑)

  • 删除修复情况2:当前结点是黑加黑且兄弟是黑色且兄弟结点的两个子结点全为黑色

  • 删除修复情况3:当前结点颜色是黑+黑,兄弟结点是黑色,兄弟的左子是红色,右子是黑色

  • 删除修复情况4:当前结点颜色是黑-黑色,它的兄弟结点是黑色,但是兄弟结点的右子是红色,兄弟结点左子的颜色任意

此时,我们需要调用RB-DELETE-FIXUP(T, x),来恢复与保持红黑性质的工作。

下面,咱们便来分别处理这4种删除修复情况。

删除修复情况1: 当前结点是黑+黑且兄弟结点为红色(此时父结点和兄弟结点的子结点分为黑)。

解法:把父结点染成红色,把兄弟结点染成黑色,之后重新进入算法(我们只讨论当前结点是其父结点左孩子时的情况)。此变换后原红黑树性质5不变,而把问题转化为兄弟结点为黑色的情况(注:变化前,原本就未违反性质5,只是为了把问题转化为兄弟结点为黑色的情况)。 即如下代码操作:

//调用RB-DELETE-FIXUP(T, x) 的1-8行代码
 1 while x ≠ root[T] and color[x] = BLACK
 2     do if x = left[p[x]]
 3           then w ← right[p[x]]
 4                if color[w] = RED
 5                   then color[w] ← BLACK                        ▹  Case 1
 6                        color[p[x]] ← RED                       ▹  Case 1
 7                        LEFT-ROTATE(T, p[x])                    ▹  Case 1
 8                        w ← right[p[x]]                         ▹  Case 1

变化前:

 

变化后:

 

删除修复情况2: 当前结点是黑加黑且兄弟是黑色且兄弟结点的两个子结点全为黑色。

解法:把当前结点和兄弟结点中抽取一重黑色追加到父结点上,把父结点当成新的当前结点,重新进入算法。(此变换后性质5不变),即调用RB-INSERT-FIXUP(T, z) 的第9-10行代码操作,如下:

//调用RB-DELETE-FIXUP(T, x) 的9-11行代码
9                if color[left[w]] = BLACK and color[right[w]] = BLACK
10                   then color[w] ← RED                          ▹  Case 2
11                        x p[x]                                  ▹  Case 2

变化前:

 

变化后:

 

删除修复情况3: 当前结点颜色是黑+黑,兄弟结点是黑色,兄弟的左子是红色,右子是黑色。

解法:把兄弟结点染红,兄弟左子结点染黑,之后再在兄弟结点为支点解右旋,之后重新进入算法。此是把当前的情况转化为情况4,而性质5得以保持,即调用RB-INSERT-FIXUP(T, z) 的第12-16行代码,如下所示:

//调用RB-DELETE-FIXUP(T, x) 的第12-16行代码
12                   else if color[right[w]] = BLACK
13                           then color[left[w]] ← BLACK          ▹  Case 3
14                                color[w] ← RED                  ▹  Case 3
15                                RIGHT-ROTATE(T, w)              ▹  Case 3
16                                w ← right[p[x]]                 ▹  Case 3

变化前:

 

变化后:

 

删除修复情况4: 当前结点颜色是黑-黑色,它的兄弟结点是黑色,但是兄弟结点的右子是红色,兄弟结点左子的颜色任意。

解法:把兄弟结点染成当前结点父结点的颜色,把当前结点父结点染成黑色,兄弟结点右子染成黑色,之后以当前结点的父结点为支点进行左旋,此时算法结束,红黑树所有性质调整正确,即调用RB-INSERT-FIXUP(T, z)的第17-21行代码,如下所示:

//调用RB-DELETE-FIXUP(T, x) 的第17-21行代码
17                         color[w] ← color[p[x]]                 ▹  Case 4
18                         color[p[x]] ← BLACK                    ▹  Case 4
19                         color[right[w]] ← BLACK                ▹  Case 4
20                         LEFT-ROTATE(T, p[x])                   ▹  Case 4
21                         x ← root[T]                            ▹  Case 4

变化前:

 

变化后:

 

本文参考

本文参考了算法导论、STL源码剖析、计算机程序设计艺术等资料。

 

最后

欢迎大家一起交流,喜欢文章记得点个赞哟,感谢支持!欢迎大家关注我的公种浩【程序员追风】,整理了2019年多家公司java面试题资料100多页pdf文档,文章都会在里面更新,整理的资料也会放在里面。

算法导论 - 红黑树及红黑树平衡的详细解析个人理解 算法导论 - 红黑树及红黑树平衡的详细解析个人理解 红黑树一种数据结构,效率极高,插入和删除的操作的时间复杂度都是logn,虽然红黑树对于前端来说基本很少用到,但是作为一个目标是全栈的程序员来说,算法的学习是很必要的,so … 图是"借的",字都是自己打的,也依据了自己的理解 数组 链表 数组特点是元素在内存中紧挨着存储,因而优点是定位快(O(1)),缺点是插入删除慢(O(n));而链表则不... 阅读详情

相关推荐

【数据结构】红黑树超详解 ---一篇通关红黑树原理(含源码解析+动态构建红黑树)

【数据结构】红黑树超详解 ---一篇通关红黑树底层原理(含源码解析+动态构建红黑树)

2402_84949062的博客 7776

红黑树原理简单解析

一、红黑树为什么会出现呢? 是因为二叉搜索树有可能会出现极端的情况,就是只有一侧有数据,那这样的话就会降级为链表。后来出现了平衡二叉树,但是由于强制平衡所导致付出的代价比较高昂,所以黑红树出现了。 二、简介 红黑树(Red Black Tree) 的实现是基于二叉查找树的,对于含有n个节点的二叉查找树的最坏的情况是这n个节点形成一条单链,此时二叉查找树的高度为n,时间复杂度为O(n)。为了维持O(lg n)的运行时间,就需要采取一些措施在不影响二叉查找树的性质下改变二叉查找树的结构,使之平衡。红黑树就是这样

黄沙客栈 1万+

Dofbot机械臂从零部署笔记(1)——ROS之创建URDF模型、配置Moveit和MotionPlanning

Dofbot机械臂从零部署笔记(1)——ROS之创建URDF模型、配置Moveit和MotionPlanning

takedachia的博客 8697

红黑树----原理及详细实现过程

1.红黑树的概念 红黑树,是一种二叉搜索树,但在每个结点上增加一个存储位表示结点的颜色,可以是Red或Black。 通过对任何一条从根到叶子的路径上各个结点着色方式的限制,红黑树确保没有一条路径会比其他路径长出两倍,因而是接近平衡的。 2.红黑树的性质 每个节点不是红色就是黑色 根结点是黑色的 如果一个结点是红色的,则它的两个孩子是黑色的 对于每个节点,从该节点到其后代叶结点的简单路径上,均包...

lovehang99的博客 4474

数据库崩溃恢复机制:REDO日志与UNDO日志详解

在数据库管理系统中,事务的持久性和一致性是至关重要的。为了保证在系统崩溃或发生故障时数据不会丢失,数据库引入了恢复机制。其中,REDO日志和UNDO日志是两种关键的恢复技术。本文将详细描述这两种日志的作用、工作原理以及它们在数据库崩溃恢复中的应用。

My_wife_QBL的博客 1411

基于相位检测与锁相环的DIY超声波风速仪设计与实现

相位检测是信号处理中的一项基础技术,它通过测量两个周期信号之间的相位差来获取信息。其核心原理在于,当信号在介质中传播时,外部因素(如风速)会改变其传播速度,从而导致接收端信号的相位发生偏移。锁相环(PLL)是实现高精度相位检测的关键电路,它能动态调整本地振荡器的频率,以跟踪并锁定输入信号的相位,从而将微小的相位变化转换为易于测量的电压信号。这项技术的工程价值在于其对电路精度要求相对较低,鲁棒性好,非常适合低成本、高灵敏度的测量应用。在气象监测、工业传感和消费电子等领域,相位检测技术被广泛用于风速风向测量、位

weixin_29540599的博客 387

【图文详解】彻底了解红黑树底层实现原理

红黑树定义 红黑树(Red Black Tree) 是一种自平衡二叉查找树,是在计算机科学中用到的一种数据结构,典型的用途是实现关联数组。 红黑树是一种特化的AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,从而获得较高的查找性能。 它虽然是复杂的,但它的最坏情况运行时间也是非常良好的,并且在实践中是高效的: 它可以在O(log n)时间内做查找,插入和删除,这里的n是树中元素的数目。(来源百度百科) 结构特点 ...

GodTheTang的博客 4705

基于STM32的简易电压、频率、占空比测量仪

任务详解:我们需要实现三个功能: 第一个是测频率的功能,输入信号为频率为1kHz~10kHz的三种波形(方波、三角波、正弦波),这也是我们最常见的三种波形。幅度是不需要测量的,所以先不关心幅度。 第二个是电压报警功能,当输入信号的幅度超过15V时,我们需要报警(这里我用了一个红色LED当报警器,有条件的还可以加个蜂鸣器)并且把此时的信号幅度显示在OLED屏幕上。 第三个是通信方式的要求,需要有串口通信功能。 第四个是计算占空比的任务,这个任务的输入是幅度为1V~10V、频率为20Hz ~1kHz的方波,需要

weixin_43469377的博客 7251

【数据结构】17张图带你解析红黑树的原理!保证你能看懂!

二叉查找树 ...

Together 4158

大疆sdkV4以及V5webrtc推流

webrtcSDK中要用它的VideoCapturer来初始化推流 ViewdoCapturer是一个接口我们继承它 然后重建创建文件CapturerAircraft.java文件。CameraUtil.java这个是我们用来创建CapturerAircraft的一个工具。除了CapturerAircraft这个文件不一样外其他都是一样的。PlayBodyBean.java这个是我们的配置文件。下面的有个代码块第一个是v4的 第二个是v5的。接着我们来创建几个推流用到的webrtc工具。

weixin_41624865的博客 1188

5.1 rocr-runtime libhsakmt event 机制全景—从 Doorbell 到 Signal

本文介绍了ROCm/KFD中的Event机制架构,主要解决GPU异步计算中的反馈问题。Event机制分为三层:硬件层触发事件,内核层管理事件通知,用户态接收处理。事件类型分为三大类:同步类事件(如SIGNAL、QUEUE_EVENT)用于任务完成通知,异常类事件(如MEMORY、HW_EXCEPTION)报告错误,拓扑类事件(如NODECHANGE)处理设备变更。该机制通过共享内存和中断实现高效通信,避免CPU轮询开销。后续文章将深入内核实现、Thunk层封装和多队列完成检测实战。

图形/计算GPU的驱动与虚拟化技术分享园地 1306

红黑树维持平衡的方式解析

在对红黑树进行添加或者删除操作时可能会破坏这些特点,所以红黑树采取了很多方式来维护这些特点,从而维持平衡。主要通过修改颜色(颜色反转)和旋转节点(左旋转、右旋转)来完成平衡。 左旋(RotateLeft) 逆时针旋转红黑树的两个结点,使得父结点被自己的右孩子取代,而自己成为自己的左孩子 上图所示过程如下: 1. 以X为基点逆时针旋转 2. X的父节点被x原来的右孩子Y取代 3. c保持不变 4. Y节点原来的左孩子c变成X的右孩子 动态过程如下 右旋(RotateRight) 顺.

xiaosong_2016的博客 8480

Python爬虫实战:基于最新技术栈的晋江文学城小说爬取

本文将详细介绍如何使用Python最新技术栈构建一个高效的晋江文学城小说爬虫。我们将从爬虫基础知识讲起,逐步深入到反爬机制应对、分布式爬虫设计以及数据存储优化等高级话题。文章包含完整的代码实现,使用aiohttp+asyncio实现异步爬取,Playwright处理动态渲染,并结合Redis实现分布式任务队列。本文旨在为读者提供一个全面的现代Python爬虫开发指南。网络爬虫(Web Crawler)是一种自动抓取互联网信息的程序,广泛应用于搜索引擎、数据分析和内容聚合等领域。

2201_76125261的博客 870

红黑树的描述与解析

今天我们来了解一下红黑树的结构特性与优势对比,在这之前我们首先应该知道二叉查找树和AVL树: 二叉查找树: 1.左子树上所有结点的值均小于或等于它的根结点的值。 2.右子树上所有结点的值均大于或等于它的根结点的值。 3.左、右子树也分别为二叉排序树,如下图: 这就是一棵简单的二叉查找树,从上面的图中可以看出他也是一棵简单的排序二叉树,但这种情况会产生一种最坏的情况当重新组合这些数让他们上来就按照一定顺序进行排序再插入,这样就破坏了他的平衡,成了一个所谓的“单一”生物,如图: 此时我们便加入了平衡二叉树(

蜗牛_在努力的博客 481

【CAN总线测试】——使用VH6501 进行Busoff快慢恢复测试

即在IDBase中填标准帧11位二进制ID,如果不满11位,高位补零,凑齐11位,并将下方CurrTriggerField设置干扰前一位AckSlot位,Triggeroffset设置为0。首先确认环境支持VN6501,6501硬件连接成功后,工程的小闪电是正常黄色状态,若是灰色证明缺少License,或者软件安装环境不正确。填写干扰报文ID、干扰位置 注意这里的干扰位置是实际干扰的前一位,TriggerOffset表示偏移量,可以干扰某个字节的具体位.找到并打开Panel后,开启工程,并且配置面板。

LOVE135149的博客 5630

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

红黑树添加和删除节点原理

红黑树原理及插入、删除节点原理

平生我自知的博客 1118

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

红黑树原理以及插入、删除算法 附图例说明

一、概念 R-B Tree,全称是Red-Black Tree又称红黑树,它是一种特殊的二叉查找树,红黑树的每个节点上都有存储位表示节点的颜色,可以是红或黑。 二、特性 1、每个节点或者是红色,或者是黑色 2、根节点是黑色的 3、每个叶子节点(NIL)是黑色的。注意:这里的叶子节点,...

384

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

红黑树

介绍另一种平衡二叉树:红黑树(Red Black Tree),红黑树由Rudolf Bayer于1972年发明,当时被称为平衡二叉B树(symmetric binary B-trees),1978年被Leonidas J. Guibas 和Robert Sedgewick改成一个比较摩登的名字:红黑树。 红黑树和之前所讲的AVL树类似,都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡,

UPON_THE_YUN 873

C++学习记录(17)红黑树简单实现map和set

风风火火的写了写AVL树和红黑树的实现,其实都是铺垫,早在map和set使用的学习的时候,我就提库里的map和set底层封装个红黑树实现。为了能够自己模拟实现map和set的一些功能:我们先学了AVL树,重点关注了保证平衡方式和平衡调整,也就是四种旋转,这为后续写红黑树实现又做铺垫;有了AVL树的铺垫,在学习红黑树时重点学习红黑树如何保证平衡,另外的重点就是调整用到旋转的时候就不用另起炉灶,直接复用AVL树中实现的就行了。那么怎么用红黑树实现map和set的功能呢?

2401_85751795的博客 867

算法导论 之 红黑树 - 添加[C语言]

作者:邹祁峰 邮箱:Qifeng.zou.job@hotmail.com 博客:http://blog.youkuaiyun.com/qifengzou 日期:2013.12.24 21:00 转载请注明来自"祁峰"的优快云博客 1 引言 在之前的博文中,本人对平衡二叉树的处理做了较详尽的分析,有兴趣的朋友可以参阅博文《算法导论 之 平衡二叉树 - 创建 插入 搜索...

做事先做人! 4824

2-3树 红黑树 简介+实现

2-3树 从根节点到任意一个叶子节点经过的节点数量 算法导论 中的红黑树: 红黑树是一棵具有下列特性的二分搜索树: 每个节点或者是红色, 或者是黑色 根节点是黑色的 每一个叶子节点 (这里的叶子节点指最后的空节点) 是黑色的 如果一个节点是红色的, 那么他的孩子节点都是黑色的 从任意一个节点到叶子节点, 经过的黑色节点是一样的 ...

Jason 741
上一篇: 要想深入理解mysql索引?这16个点你必须要了解!
下一篇: Java线程池原理与源码详细解读,再也不怕面试问线程池了!
程序员追风
程序员追风 优质创作者: Java技术领域 优质创作者: Java技术领域
博客等级 码龄7年 9635粉丝 237原创
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值