深入了解红黑树

JAVA学习笔记

1. 什么是查找树?

2. 为什么引入树?

3. 什么是红黑树?

1.查找树(搜索树)
一棵树是一些结点的集合。这个集合可以是空;若非空,则一棵树由称作根的结点r以及0个或多个非空的(子)树T1,T2,…,Tk组成,这些子树中每一棵的根都被来自根r的一条有向的边连接。
仿佛树生枝发芽一般,简图如下:
树图
2.为什么引入树?
因为表的线性访问时间太慢。

那…重点来了 什么叫个
3.红黑树?
先来了解一下二叉树 ,(转接)
总的来说就是一点 根据某一值将数据分成两大门派,类似二分查找(突然想起这个高大的词,有点张冠李戴,没错我就秀秀词汇)

红黑树全称:red-black Tree ,是一种特殊的二叉查找树。红黑树的每个节点都有存储位表示节点的颜色,可以是红或黑
红黑树的特性:
(1)每个节点或者是黑色,或者是红色。
(2)根节点是黑色。
(3)每个叶子节点(NIL)是黑色。
[注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
(4)如果一个节点是红色的,则它的子节点必须是黑色的。
(5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
简图:red-blackTree

3.1红黑树的应用:
红黑树的应用比较广泛,主要是用它来存储有序的数据,它的时间复杂度是O(lgn),效率非常之高。
例如,Java集合中的TreeSet和TreeMap,C++ STL中的set、map,以及Linux虚拟内存的管理,都是通过红黑树去实现的。

3.2红黑树的基本操作 左旋和右旋
红黑树的基本操作是添加、删除。在对红黑树进行添加或删除之后,都会用到旋转方法。为什么呢?道理很简单,添加或删除红黑树中的节点之后,红黑树就发生了变化,可能不满足红黑树的5条性质,也就不再是一颗红黑树了,而是一颗普通的树。而通过旋转,可以使这颗树重新成为红黑树。简单点说,旋转的目的是让树保持红黑树的特性。
旋转包括两种:左旋 和 右旋。下面分别对它们进行介绍。
左旋
在这里插入图片描述
对x进行左旋,意味着"将x变成一个左节点"。
左旋的伪代码《算法导论》:参考上面的示意图和下面的伪代码,理解“红黑树T的节点x进行左旋”是如何进行的。

LEFT-ROTATE(T, x)  
 y ← right[x]            // 前提:这里假设x的右孩子为y。下面开始正式操作
   right[x] ← left[y]      // 将 “y的左孩子” 设为 “x的右孩子”,即 将β设为x的右孩子
  p[left[y]] ← x          // 将 “x” 设为 “y的左孩子的父亲”,即 将β的父亲设为x
  p[y] ← p[x]             // 将 “x的父亲” 设为 “y的父亲”
 if p[x] = nil[T]       
  then root[T] ← y                 // 情况1:如果 “x的父亲” 是空节点,则将y设为根节点
 else if x = left[p[x]]  
            then left[p[x]] ← y    // 情况2:如果 x是它父节点的左孩子,则将y设为“x的父节点的左孩子”
           else right[p[x]] ← y   // 情况3:(x是它父节点的右孩子) 将y设为“x的父节点的右孩子”
  left[y] ← x             // 将 “x” 设为 “y的左孩子”
  p[x] ← y                // 将 “x的父节点” 设为 “y”

理解左旋之后,看看下面一个更鲜明的例子。你可以先不看右边的结果,自己尝试一下。
在这里插入图片描述
右旋
对x进行左旋,意味着"将x变成一个左节点"。

右旋的伪代码《算法导论》:参考上面的示意图和下面的伪代码,理解“红黑树T的节点y进行右旋”是如何进行的。

RIGHT-ROTATE(T, y)  
  x ← left[y]             // 前提:这里假设y的左孩子为x。下面开始正式操作
  left[y] ← right[x]      // 将 “x的右孩子” 设为 “y的左孩子”,即 将β设为y的左孩子
  p[right[x]] ← y         // 将 “y” 设为 “x的右孩子的父亲”,即 将β的父亲设为y
  p[x] ← p[y]             // 将 “y的父亲” 设为 “x的父亲”
  if p[y] = nil[T]       
  then root[T] ← x                 // 情况1:如果 “y的父亲” 是空节点,则将x设为根节点
  else if y = right[p[y]]  
            then right[p[y]] ← x   // 情况2:如果 y是它父节点的右孩子,则将x设为“y的父节点的左孩子”
            else left[p[y]] ← x    // 情况3:(y是它父节点的左孩子) 将x设为“y的父节点的左孩子”
  right[x] ← y            // 将 “y” 设为 “x的右孩子”
  p[y] ← x                // 将 “y的父节点” 设为 “x”

理解右旋之后,看看下面一个更鲜明的例子。

在这里插入图片描述

旋转总结:

(01) 左旋 和 右旋 是相对的两个概念,原理类似。理解一个也就理解了另一个。

(02) 下面谈谈如何区分 左旋 和 右旋。
在实际应用中,若没有彻底理解 左旋 和 右旋,可能会将它们混淆。下面谈谈我对如何区分 左旋 和 右旋 的理解。
区分 左旋 和 右旋

仔细观察上面"左旋"和"右旋"的示意图。我们能清晰的发现,它们是对称的。无论是左旋还是右旋,被旋转的树,在旋转前是二叉查找树,并且旋转之后仍然是一颗二叉查找树。

在这里插入图片描述

左旋示例图(以x为节点进行左旋):

                           z

x /
/ \ --(左旋)–> x
y z /
y
对x进行左旋,意味着,将“x的右孩子”设为“x的父亲节点”;即,将 x变成了一个左节点(x成了为z的左孩子)!。 因此,左旋中的“左”,意味着“被旋转的节点将变成一个左节点”。

右旋示例图(以x为节点进行右旋):

                           y

x \
/ \ --(右旋)–> x
y z
z
对x进行右旋,意味着,将“x的左孩子”设为“x的父亲节点”;即,将 x变成了一个右节点(x成了为y的右孩子)! 因此,右旋中的“右”,意味着“被旋转的节点将变成一个右节点”。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值