数据结构必知 --- 红黑树

简介: 数据结构必知 --- 红黑树

1、什么是红黑树?



红黑树是一棵二叉搜索树,并且这棵树实现了弱平衡(严格的平衡是对于树中任意节点的左右子树高度差不超过1),确保没有一条路径会比其他路径长出两倍。弱平衡是指任意节点到到其子树中的叶节点所经过的黑色节点数相同,也可以称为黑色平衡(因为严格平衡的二叉搜索树AVL,维护成本太高,所以退而求其次)。


红黑树的每个节点上都有存储位表示节点的颜色,可以是红(Red)或黑(Black)。红黑树最坏情况可以在O(log n)时间内完成查找,插入和删除操作。


2、为什么要引入红黑树?



  • 普通的二叉搜索树可能退化为链表的情况,查询的效率将大大降低。
  • 引入强平衡(树中任意节点的左右子树高度差不超过1),形如AVL树,树的维护成本太高。ps:windows对进程地址空间的管理用到了AVL树。


红黑树是牺牲了严格的高度平衡的优越条件为代价(统计性能更高),它只要求部分地达到平衡要求,降低了对旋转的要求,从而提高了性能。


相对于要求严格的AVL树来说,它的旋转次数少,插入数据分布比较好用AVL树,数据分布复杂用红黑树。


3、红黑树的五大特性



  • 节点是红色或者是黑色
  • 根节点是黑色
  • 每个叶节点(NIL或空节点)是黑色
  • 每个红色节点的两个子节点都是黑色的(即不能有两个红节点相连)
  • 任意节点到其子树中的叶节点(NIL节点)所经过的黑色节点数相同(最重要)


ps:注:红黑树引入了NIL节点,是一种虚拟节点,用于填充节点的左或右为空的位置,主要目的是便于判断从根节点到NIL节点,经过了多少黑节点。因此红黑树中的叶节点特指NIL节点,颜色必须是黑色。


4、红黑树的插入和删除



红黑树调整的两种方式:


  • 旋转(左旋和右旋):
  • 左旋:逆时针旋转红黑树的两个节点,使得父节点被自己的右孩子取代,而自己成为自己的左孩子。
  • 右旋:顺时针旋转红黑树的两个节点,使得父节点被自己的左孩子取代,而自己成为自己的右孩子。
  • 变色(可能会产生连锁反应,多节点变色,目的是维持性质5)


添加操作(最多旋转两次):


(1)将红黑树当作一颗二叉查找树,将节点插入。

(2)将插入的节点着色为"红色"。ps:如果插入是黑色节点,破坏第五条性质。

(3)通过一系列的旋转或着色等操作,使之重新成为一颗红黑树(满足红黑树的五个性质)。


删除操作(最多旋转三次):


(1)将红黑树当作一颗二叉查找树,将节点删除。

(2)通过"旋转和重新着色"等一系列来修正该树,使之重新成为一棵红黑树。


ps:插入和删除设计很多情况,可以参考维基百科学习。


5、应用



  • 广泛用于C++的STL中,map和set都是用红黑树实现的.
  • 著名的linux进程调度Completely Fair Scheduler,用红黑树管理进程控制块,进程的虚拟内存区域都存储在一颗红黑树上,每个虚拟地址区域都对应红黑树的一个节点,左指针指向相邻的地址虚拟存储区域,右指针指向相邻的高地址虚拟地址空间.
  • IO多路复用epoll的实现采用红黑树组织管理sockfd(事件块),以支持快速的增删改查.
  • ngnix中,用红黑树管理timer,因为红黑树是有序的,可以很快的得到距离当前最小的定时器.
  • java中TreeMap的实现.
相关文章
232.用栈实现队列,225. 用队列实现栈
在232题中,通过两个栈(`stIn`和`stOut`)模拟队列的先入先出(FIFO)行为。`push`操作将元素压入`stIn`,`pop`和`peek`操作则通过将`stIn`的元素转移到`stOut`来实现队列的顺序访问。 225题则是利用单个队列(`que`)模拟栈的后入先出(LIFO)特性。通过多次调整队列头部元素的位置,确保弹出顺序符合栈的要求。`top`操作直接返回队列尾部元素,`empty`判断队列是否为空。 两题均仅使用基础数据结构操作,展示了栈与队列之间的转换逻辑。
|
编译器 C语言 C++
栈区的非法访问导致的死循环(x64)
这段内容主要分析了一段C语言代码在VS2022中形成死循环的原因,涉及栈区内存布局和数组越界问题。代码中`arr[15]`越界访问,修改了变量`i`的值,导致`for`循环条件始终为真,形成死循环。原因是VS2022栈区从低地址到高地址分配内存,`arr`数组与`i`相邻,`arr[15]`恰好覆盖`i`的地址。而在VS2019中,栈区先分配高地址再分配低地址,因此相同代码表现不同。这说明编译器对栈区内存分配顺序的实现差异会导致程序行为不一致,需避免数组越界以确保代码健壮性。
302 0
栈区的非法访问导致的死循环(x64)
|
算法 调度 C++
STL——栈和队列和优先队列
通过以上对栈、队列和优先队列的详细解释和示例,希望能帮助读者更好地理解和应用这些重要的数据结构。
411 11
☀☀☀☀☀☀☀有关栈和队列应用的oj题讲解☼☼☼☼☼☼☼
### 简介 本文介绍了三种数据结构的实现方法:用两个队列实现栈、用两个栈实现队列以及设计循环队列。具体思路如下: 1. **用两个队列实现栈**: - 插入元素时,选择非空队列进行插入。 - 移除栈顶元素时,将非空队列中的元素依次转移到另一个队列,直到只剩下一个元素,然后弹出该元素。 - 判空条件为两个队列均为空。 2. **用两个栈实现队列**: - 插入元素时,选择非空栈进行插入。 - 移除队首元素时,将非空栈中的元素依次转移到另一个栈,再将这些元素重新放回原栈以保持顺序。 - 判空条件为两个栈均为空。
|
存储 C语言 C++
【C++数据结构——栈与队列】顺序栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现顺序栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 1.初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储
1202 77
|
C++
【C++数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】
【数据结构——栈和队列】括号配对(头歌实践教学平台习题)【合集】(1)遇到左括号:进栈Push()(2)遇到右括号:若栈顶元素为左括号,则出栈Pop();否则返回false。(3)当遍历表达式结束,且栈为空时,则返回true,否则返回false。本关任务:编写一个程序利用栈判断左、右圆括号是否配对。为了完成本关任务,你需要掌握:栈对括号的处理。(1)遇到左括号:进栈Push()开始你的任务吧,祝你成功!测试输入:(()))
617 7
|
存储 C++ 索引
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
【数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】初始化队列、销毁队列、判断队列是否为空、进队列、出队列等。本关任务:编写一个程序实现环形队列的基本运算。(6)出队列序列:yzopq2*(5)依次进队列元素:opq2*(6)出队列序列:bcdef。(2)依次进队列元素:abc。(5)依次进队列元素:def。(2)依次进队列元素:xyz。开始你的任务吧,祝你成功!(4)出队一个元素a。(4)出队一个元素x。
609 13
【C++数据结构——栈与队列】环形队列的基本运算(头歌实践教学平台习题)【合集】
|
存储 C语言 C++
【C++数据结构——栈与队列】链栈的基本运算(头歌实践教学平台习题)【合集】
本关任务:编写一个程序实现链栈的基本运算。开始你的任务吧,祝你成功!​ 相关知识 初始化栈 销毁栈 判断栈是否为空 进栈 出栈 取栈顶元素 初始化栈 概念:初始化栈是为栈的使用做准备,包括分配内存空间(如果是动态分配)和设置栈的初始状态。栈有顺序栈和链式栈两种常见形式。对于顺序栈,通常需要定义一个数组来存储栈元素,并设置一个变量来记录栈顶位置;对于链式栈,需要定义节点结构,包含数据域和指针域,同时初始化栈顶指针。 示例(顺序栈): 以下是一个简单的顺序栈初始化示例,假设用C语言实现,栈中存储整数,最大
402 9
|
存储 缓存 算法
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式
在C语言中,数据结构是构建高效程序的基石。本文探讨了数组、链表、栈、队列、树和图等常见数据结构的特点、应用及实现方式,强调了合理选择数据结构的重要性,并通过案例分析展示了其在实际项目中的应用,旨在帮助读者提升编程能力。
609 5
|
算法
数据结构之购物车系统(链表和栈)
本文介绍了基于链表和栈的购物车系统的设计与实现。该系统通过命令行界面提供商品管理、购物车查看、结算等功能,支持用户便捷地管理购物清单。核心代码定义了商品、购物车商品节点和购物车的数据结构,并实现了添加、删除商品、查看购物车内容及结算等操作。算法分析显示,系统在处理小规模购物车时表现良好,但在大规模购物车操作下可能存在性能瓶颈。
501 0

热门文章

最新文章