
◆ 博主名称: 晓此方-CSDN博客 大家好,欢迎来到晓此方的博客。
⭐️C++系列个人专栏: 主题曲:C++程序设计
⭐️ 踏破千山志未空,拨开云雾见晴虹。 人生何必叹萧瑟,心在凌霄第一峰
### 目录
- [@[TOC](目录)](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [概要&序論](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [一,认识红黑树](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [1.1红黑树的概念](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [1.2红黑树的原则:四条规则交错编织出的平衡](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [Tips:关于"路径"的理解(高频易错点)](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [Tips:红黑树如何确保最长路径不超过最短路径的2倍](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [1.3红黑树的效率:相当高效的查找算法](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [二,红黑树的插入](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.1总体结构搭建:红黑树与AVL树的结构有何不同](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.2红黑树插入一个值的大概过程](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [Tips:所有情况的总图](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.3情况一:u存在且为红------直接变色](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.3.1c是新增结点](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.3.2c不是新增结点的抽象分析](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.3.3c不是新增结点的具体分析](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.3.3.1情况一:hb == 0](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.3.3.2情况二:hb == 1](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.3.3.3情况二:hb==2](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.4情况二:u不存在或存在且为黑+直线结构------变色+单旋](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.4.1c是新增结点](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [Tips:不可能出现的情况](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [Tips.1:为什么u不存在的时候,c一定是新增结点?](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [Tips.2:为什么u存在且为黑的时候,c一定不是新增结点?](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.4.2不是新增结点的情况](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.5情况三:u不存在或存在且为黑+折线结构------变色+双旋](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.5.1c是新插入结点](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.5.2c不是新插入结点](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [2.6红黑树插入的代码](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [三,红黑树的验证](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [3.1红黑树验证原理](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [3.2红黑树验证代码](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
- [四,红黑树完整代码](#目录 @TOC 概要&序論 一,认识红黑树 1.1红黑树的概念 1.2红黑树的原则:四条规则交错编织出的平衡 Tips:关于“路径”的理解(高频易错点) Tips:红黑树如何确保最长路径不超过最短路径的2倍 1.3红黑树的效率:相当高效的查找算法 二,红黑树的插入 2.1总体结构搭建:红黑树与AVL树的结构有何不同 2.2红黑树插入一个值的大概过程 Tips:所有情况的总图 2.3情况一:u存在且为红——直接变色 2.3.1c是新增结点 2.3.2c不是新增结点的抽象分析 2.3.3c不是新增结点的具体分析 2.3.3.1情况一:hb == 0 2.3.3.2情况二:hb == 1 2.3.3.3情况二:hb==2 2.4情况二:u不存在或存在且为黑+直线结构——变色+单旋 2.4.1c是新增结点 Tips:不可能出现的情况 Tips.1:为什么u不存在的时候,c一定是新增结点? Tips.2:为什么u存在且为黑的时候,c一定不是新增结点? 2.4.2不是新增结点的情况 2.5情况三:u不存在或存在且为黑+折线结构——变色+双旋 2.5.1c是新插入结点 2.5.2c不是新插入结点 2.6红黑树插入的代码 三,红黑树的验证 3.1红黑树验证原理 3.2红黑树验证代码 四,红黑树完整代码)
概要&序論
这里是此方,好久不见。 在上一篇中,我们看到了AVL树对"绝对平衡 "的极致追求。但问题是:这种严格,真的值得吗?频繁旋转的代价,让它在工程中显得过于"理想化"。于是,红黑树给出了另一种答案------不追求完美,但更高效。
让我们现在开始吧!
本文代码示例及测试所需要的头文件:
cpp
#pragma once
#include<iostream>
#include<cassert>
#include<vector>
using namespace std;
一,认识红黑树
1.1红黑树的概念
红黑树首先是一棵二叉搜索树 ,相比AVL树他的每个结点增加一个存储位来表示结点的颜色而不是平衡因子,可以是红色或者黑色 。通过对任何一条从根到叶子的路径上各个结点的颜色进行约束,红黑树确保没有一条路径会比其他路径长出2倍,因而是接近平衡的。
1.2红黑树的原则:四条规则交错编织出的平衡
- 每个结点不是红色就是黑色。
- 根结点是黑色的。
- 如果一个结点是红色的,则它的两个孩子结点必须是黑色的,也就是说任意一条路径不会有连续的红色结点。
- 对于任意一个结点,从该结点到其所有NULL结点的简单路径上,均包含相同数量的黑色结点。
满足以上四条原则的搜索二叉树就是红黑树,下面的图片中的树都是红黑树。

《算法导论》等书籍上补充了第五条规则:每个叶子结点(NIL)都是黑色的。
他这里所指的叶子结点不是传统的意义上的叶子结点,而是我们说的空结点 ,有些书籍上也把NIL叫做外部结点。NIL是为了方便准确的标识出所有路径,《算法导论》在后续讲解实现的细节中也忽略了NIL结点,所以我们知道一下这个概念即可。

Tips:关于"路径"的理解(高频易错点)
在树结构中,"路径"的定义非常容易被误解。很多人会认为:路径数 = 叶子结点个数 。例如下图中有 4 个叶子结点,于是误以为有 4 条路径------这是错误的。
正确理解是:路径必须延伸到"空结点(NULL)"。
也就是说,每一条从根节点出发,最终走到一个空指针的位置,才算一条完整路径。
因此,路径的数量实际上等于空子树(NULL指针)的数量。在该例中,共有 9 个空结点,所以路径数应为 9 条,而不是 4 条。

以上图为例,路径示意图:

Tips:红黑树如何确保最长路径不超过最短路径的2倍
由规则4可知,从根到NULL结点的每条路径都有相同数量的黑色结点,所以极端场景下,最短路径就是全是黑色结点的路径 ,假设最短路径长度为bh(就是black height)。
再结合规则2和规则3可知,任意一条路径不会有连续的红色结点 ,所以极端场景下,最长的路径就是一黑一红间隔组成,那么最长路径的长度为2* bh。
好!我们再结合红黑树的4点规则,理论上的全黑最短路径和一黑一红的最长路径并不是在每棵红黑树都存在的 。假设任意一条从根到NULL结点路径的长度为x,那么bh <= h <= 2* bh。
这就是---------红黑树确保最长路径不超过最短路径2倍的秘诀。
1.3红黑树的效率:相当高效的查找算法
假设N是红黑树树中结点数量,h是最短路径的长度,那么我们就能得到公式:

由此推出 h≈logN,也就是说意味着红黑树增删查改最坏也就是走最长路径2∗logN 那么时间复杂度还是 O(logN)。

红黑树的表达相对AVL树要抽象一些,AVL树通过高度差直观地控制了平衡。红黑树通过4条规则的颜色约束,间接地实现了近似平衡,它们效率都是同一档次,但是相对而言,插入相同数量的结点,红黑树的旋转次数是更少的,因为他对平衡的控制没那么严格。
二,红黑树的插入
2.1总体结构搭建:红黑树与AVL树的结构有何不同
cpp
using namespace std;
enum Color{//多了一个颜色枚举
RED,
BLACK
};
template <class K,class V>
class RBTreeNode
{
public:
pair<K, V> _Date;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Color _col;//多了颜色控制
RBTreeNode(const pair<K,V>& pir)
:_Date(pir)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_col(RED)//初始化,为什么初始化RED后面会讲
{}
};
template <class K, class V>
class RBTree
{
using Node = RBTreeNode<K, V>;
public:
//.......
private:
Node* _root = nullptr;
};
2.2红黑树插入一个值的大概过程
1.首先红黑树是一颗二叉树:插入一个值按二叉搜索树规则进行插入,插入后我们只需要观察是否符合红黑树的4条规则。
2.插入的结点颜色有讲究:如果是空树插入,新增结点是黑色结点。如果是非空树插入,新增结点必须是红色结点。
解释一下这个原理
如果插入的是黑色结点:原本任何一条路径的黑色结点都是相同的,但是现在插入的结点是黑色的------那么绝对会有一条路径的黑色结点数量要比其他路径的黑色结点数量多------必然破坏规则四
如果插入的是红色结点:如果这个新结点的父亲是红色,那么就会破坏规则三,如果不是红色,就不会破坏规则三------概率破坏规则三
既然插入红色结点变成了必然,那么必然就要面对------插入红色结点后假如规则三被破坏如何补救。
说明 :下图中假设我们把
新增结点标识为c (cur),c的父亲标识为p(parent),p的父亲标识为g(grandfather),p的兄弟标识为u(uncle)。
进一步分析:
- C是红色:为了让错误必然发生------p为红色。
- C是红色,p为红色:为了让插入前红黑树成立------g必为黑。

这三个颜色都固定了,关键的变化看 u 的情况,需要根据u分为以下几种情况分别处理。 接下来就是分情况讨论啦。
Tips:所有情况的总图
红黑树的插入情况实在太复杂了 ,必须画一个图来理清楚:(图看不清可以保存一下 )

2.3情况一:u存在且为红------直接变色
2.3.1c是新增结点
方法: c为红,p为红,g为黑,u存在且为红,则将p和u变黑,g变红。在把g当做新的c,继续往上更新。

分析: 因为p和u都是红色,g是黑色,把p和u变黑,左边子树路径各增加一个黑色结点,g再变红,相当于保持g所在子树的黑色结点的数量不变,同时解决了c和p连续红色结点的问题,
需要继续往上更新是因为:
- 如果g的父亲还是红色,那么就还需要继续处理。
- 如果g的父亲是黑色,则处理结束了。
- 如果g就是整棵树的根,再把g变回黑色。
这种情况只变色,不旋转。所以无论c是p的左还是右,p是g的左还是右,都是上面的变色处理方式。
2.3.2c不是新增结点的抽象分析
以上我们仅讨论了插入节点后的一种情况。当节点 10 被染为红色后,如果其父节点仍然是红色,则需要继续向上调整,如图所示。此时,上文中的节点 10 就对应下图中的节点 x 。
为了更一般化地描述所有可能的情形,我们将子树进行抽象处理:
- 子树 a 和 b 是黑色节点高度为 bh−1的任意子树;
- 子树 d、e 、 f 是黑色节点高度为 bh的任意子树。
对这种情况仍执行相同的变色操作 :将父节点 p和叔节点 u 染黑,祖父节点g 染红。随后,将 g 视为新的"当前节点" c ,继续向上进行调整。这一过程可递归重复,直到满足红黑树的性质为止。

2.3.3c不是新增结点的具体分析
2.3.3.1情况一:hb == 0
a/b/c/d/e都是空,c为新增节点。需要注意的是,x是6和15节点的任意一个孩子,都会引发这里的变色逻辑。

2.3.3.2情况二:hb == 1
子树可能出现的情况:

hb == 1,d/e/f为hb==1的红黑树:
- c之前是黑色节点,在a和b中插入引发c变色为红色
- d/e/f为x/y/z/m中任意一种,组合为444
- a和b为红色节点,在a和b的四个孩子的任意位置插入,都会让a和b变成黑色,c变成红色,继续往上更新,插入位置有4个位置。
- 所有情况组合起来合计:444*4 = 256

2.3.3.3情况二:hb==2
可能出现的子树情况:

hb == 2,d/e/f为hb= =2的红黑树,a和b是hb= =1的根为红色的树
- d/e/f的组合为:(256+16)(256+16)(256+16) = 20123648
- a和b为根节点为红色节点的hb==1的树,这里可以看到a和b插入组合也不少
- a或者b插入至少要经历两次变色和向上处理才能得到这里的情况,这里的组合情况至少是百亿以上了。

2.4情况二:u不存在或存在且为黑+直线结构------变色+单旋
2.4.1c是新增结点
方法:
- 将p结点变黑,将g结点变红。
- 以g结点为旋转轴发生一次右单旋。

分析:当前问题是 c 和 p 同为红色,违反了红黑树"不能有连续红结点"的性质,因此必须让 p 变黑,从而直接消除这一对红红冲突。同时将 g 变红,是为了在旋转后维持整棵子树的黑高不变,否则会导致从 g 出发的不同路径黑色结点数量不一致。
是否需要继续向上? 不需要:旋转完成后,p 作为新的子树根节点为黑色,既消除了当前的红红冲突,又不会与其父节点产生新的红红冲突,同时整棵子树的黑高保持不变,因此无需继续向上调整。
Tips:不可能出现的情况
c为红,p为红,g为黑,u不存在或者u存在且为黑,先说结论:
-
u不存在,则c一定是新增结点。
-
u存在且为黑,则c一定不是新增。
Tips.1:为什么u不存在的时候,c一定是新增结点?
如图,假如c不是新增结点,那么3结点在最初的时候是黑色的,(3结点是在下面那一轮中作为爷爷结点在变色过程中变红的 )
于是10->6->3路径下就会有2个黑色结点,而10->NIL路径下只有一个黑色结点,破坏了规则四。

Tips.2:为什么u存在且为黑的时候,c一定不是新增结点?

和上面一样的道理,如果u存在且为黑,如果c是新插入的结点,那么10->6->3路径上只有一个黑色结点,10->15路径上就会有两个黑色结点,破坏了规则四。
2.4.2不是新增结点的情况
方法:
- 将p结点变黑,将g结点变红。
- 以g结点为旋转轴发生一次右单旋。
分析 :此时由于叔节点为黑,无法通过再次变色维持黑高平衡,因此必须通过旋转重构局部结构。单旋通过提升父节点、降低祖父节点,实现黑高度的重新分配,并彻底消除红红冲突,同时保证调整不会继续向上传播。

2.5情况三:u不存在或存在且为黑+折线结构------变色+双旋
2.5.1c是新插入结点
条件:c为红,p为红,g为黑,u不存在或者u存在且为黑,此时结构呈"折线型",单旋无法解决问题,需要进行双旋调整。
方法: 先以 p 为旋转轴进行一次单旋(将结构拉直),再以 g 为旋转轴进行一次单旋,最后将 c 变黑,g 变红。

分析: 由于当前结构为折线,若直接以 g 进行单旋,无法同时满足"消除红红冲突"和"保持黑高不变"两个条件。第一次围绕 p 的旋转,本质是将折线结构转换为直线结构,第二次围绕 g 的旋转,才是真正完成局部结构的重构。
将 c 变黑,这样左右黑色高度控制到插入前状态;将 g 变红,是为了保证调整前后子树右侧黑色结点数量一致。
是否需要继续向上? 不需要:子树已经局部完全平衡,且 c 作为新的根节点为黑色,不会与其父节点产生连续红色结点的问题。
2.5.2c不是新插入结点
方法: 先以 p 为旋转轴进行一次单旋(将结构拉直),再以 g 为旋转轴进行一次单旋,最后将 c 变黑,g 变红。
c之前是黑色结点,是在其子树中插入后触发了情况一(u为红,注意是情况一 ),经过变色将其染为红色,并继续向上更新,最终在当前层形成红红冲突。
条件:此时仍满足:c为红,p为红,g为黑,u不存在或者u存在且为黑,且结构为折线型。

分析 :由于叔节点为黑,无法通过再次变色维持黑高平衡,同时当前结构为折线,单旋无法完成结构调整,因此必须通过双旋来同时解决结构问题与颜色问题。第一次旋转将结构拉直,第二次旋转完成子树重构。
调整后将 c 变黑,g 变红,使 c 成为该子树新的根节点,从而保证黑色结点数量不变,并彻底消除连续红色结点的问题。
是否需要继续向上? 不需要,调整完成后子树已经满足红黑树所有性质,且新的根节点 c 为黑色,不会继续向上传播问题。
2.6红黑树插入的代码
cpp
// 右单旋:以 cur 为旋转轴进行右旋
void RotateR( Node* cur){
Node* SubL = cur->_left;// SubL:cur 的左孩子(将成为新的子树根)
Node* SubLR =cur->_left->_right;// SubLR:SubL 的右子树(旋转后会挂到 cur 的左边)
Node* pParent = cur->_parent;// 记录 cur 的父节点
if (cur == _root) // 如果当前节点是根,则更新根节点
_root = SubL;
// 右旋核心操作:SubL 上升为根,cur 下沉为其右孩子
SubL->_right = cur;
cur->_parent = SubL;
// 处理 SubLR:挂到 cur 的左子树
if(SubLR)
SubLR->_parent = cur;
cur->_left = SubLR;
// 重新连接 SubL 与原父节点
SubL->_parent=pParent;
// 如果原来有父节点,需要重新挂接
if (pParent) {
if (pParent->_Date.first > SubL->_Date.first)
pParent->_left = SubL;
else if (pParent->_Date.first < SubL->_Date.first)
pParent->_right = SubL;
}
}
// 左单旋:以 cur 为旋转轴进行左旋(RR 型)
void RotateL( Node* cur) {
// SubR:cur 的右孩子(将成为新的子树根)
Node* SubR = cur->_right;
// SubRL:SubR 的左子树(旋转后挂到 cur 的右边)
Node* SubRL = cur->_right->_left;
// 记录父节点
Node* pParent = cur->_parent;
// 如果是根节点,更新 root
if (cur == _root)
_root = SubR;
// 左旋核心操作:
// SubR 上升为根,cur 下沉为其左孩子
SubR->_left = cur;
cur->_parent = SubR;
// 处理 SubRL:挂到 cur 的右子树
cur->_right = SubRL;
if (SubRL)
SubRL->_parent = cur;
// 重新连接父节点
SubR->_parent = pParent;
if (pParent) {
if (pParent->_Date.first > SubR->_Date.first)
pParent->_left = SubR;
else if (pParent->_Date.first < SubR->_Date.first)
pParent->_right = SubR;
}
}
// 右左双旋:先右旋,再左旋(RL 型)
void RotateRL( Node* cur) {
// cur 的右孩子
Node* SubR = cur->_right;
// 中间节点(真正要上升的节点)
Node* SubRL = cur->_right->_left;
// 先对右子树做右旋(把折线拉直)
RotateR(SubR);
// 再整体左旋(完成结构调整)
RotateL(cur);
}
// 左右双旋:先左旋,再右旋(LR 型)
void RotateLR( Node* cur) {
Node* SubL = cur->_left;
Node* SubLR = cur->_left->_right;
// 先左旋(拉直结构)
RotateL(SubL);
// 再右旋(完成调整)
RotateR(cur);
}
// 插入函数(红黑树核心)
bool Insert(const pair<K, V> pir){
// 情况1:空树,直接作为根节点插入
if (_root == nullptr){
_root = new Node(pir);
_root->_col = BLACK; // 根节点必须为黑
return true;
}
Node* cur = _root;
Node* parent = nullptr;
// BST 插入过程:找到插入位置
while (cur) {
if (cur->_Date.first > pir.first) {
parent = cur;
cur = cur->_left;
}
else if (cur->_Date.first < pir.first) {
parent = cur;
cur = cur->_right;
}
else
return false; // 不允许重复键
}
// 创建新节点(默认红色)
cur = new Node(pir);
cur->_col = RED;
cur->_parent = parent;
// 挂到父节点
if (parent->_Date.first > cur->_Date.first)
parent->_left = cur;
else if (parent->_Date.first < cur->_Date.first)
parent->_right = cur;
// 开始调整(核心:解决红红冲突)
while (parent && parent->_col == RED){
Node* grandpa = parent->_parent;
// 父在左子树(LL / LR)
if (grandpa->_left == parent) {
Node* uncle = grandpa->_right;
// 情况一:叔节点存在且为红(只变色)
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandpa->_col = RED;
// 向上继续处理
cur = grandpa;
parent = grandpa->_parent;
}
// 情况二/三:叔节点不存在或为黑(旋转)
else if (uncle == nullptr || uncle->_col == BLACK) {
// LL 型 → 单旋
if (parent->_left == cur) {
RotateR(grandpa);
grandpa->_col = RED;
parent->_col = BLACK;
}
// LR 型 → 双旋
else if (parent->_right == cur) {
RotateLR(grandpa);
grandpa->_col = RED;
cur->_col = BLACK;
}
break; // 调整完成,退出
}
else assert(false);
}
// 父在右子树(RR / RL)
else if (grandpa->_right == parent) {
Node* uncle = grandpa->_left;
// 情况一:叔红 → 变色
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandpa->_col = RED;
cur = grandpa;
parent = grandpa->_parent;
}
// 情况二/三:叔黑 → 旋转
else if (uncle == nullptr || uncle->_col == BLACK) {
// RR 型 → 单旋
if (parent->_right == cur) {
RotateL(grandpa);
grandpa->_col = RED;
parent->_col = BLACK;
}
// RL 型 → 双旋
else if (parent->_left == cur) {
RotateRL(grandpa);
grandpa->_col = RED;
cur->_col = BLACK;
}
break;
}
else assert(false);
}
else assert(false);
}
// 保证根节点为黑
_root->_col = BLACK;
return true;
}
三,红黑树的验证
3.1红黑树验证原理
这里获取最长路径和最短路径,检查最长路径不超过最短路径的2倍是不可行的,因为就算满足这个条件,红黑树也可能颜色不满足规则,当前暂时没出问题,后续继续插入还是会出问题的。所以我们还是去检查4点规则,满足这4点规则,一定能保证最长路径不超过最短路径的2倍。
-
规则1枚举颜色类型,天然实现保证了颜色不是黑色就是红色。
-
规则2直接检查根即可
-
规则3前序遍历检查,遇到红色结点孩子不太方便,因为孩子有两个,且不一定存在,反过来检查父亲的颜色就方便多了。
-
规则4前序遍历,遍历过程中用形参记录跟到当前结点的blackNum,前序遍历遇到黑色结点就++blackNum,走到空就计算出一条路径的黑色结点数量 。再任意一条路径黑色结点数量作为
参考值,依次比较即可。

3.2红黑树验证代码
cpp
bool IsBalanceTree() {
// 根必须为黑
if (_root->_col == RED) return false;
int Referance = 0;
// 找一条最左路径,作为"黑高参考值"
Node* cur = _root;
while (cur) {
if (cur->_col == BLACK) Referance++;
cur = cur->_left;
}
// 从根开始递归检查
return check(_root,0,Referance);
}
private:
bool check(const Node* cur,int BlackNum,int Referance) {
// 走到空节点时,判断当前路径黑节点数是否等于参考值
if (cur == nullptr && BlackNum != Referance) return false;
else return true; // 这里会直接返回(后面逻辑不会执行)
// 检查是否存在连续红节点
if (cur->_col == RED) {
if (cur->_parent->_col == RED) {
cout << "NodeArea:" << cur->_parent->_Date.first << "->" << cur->_Date.first << "HaveContinuousRedNode" << endl;
return false;
}
}
// 黑节点计数
else if (cur->_col == BLACK) BlackNum++;
// 递归检查左右子树
return check(cur->_left, BlackNum, Referance) &&
check(cur->_right, BlackNum, Referance);
}
四,红黑树完整代码
cpp
#pragma once
#include<iostream>
#include<cassert>
#include<vector>
using namespace std;
enum Color{
RED,
BLACK
};
template <class K,class V>
class RBTreeNode
{
public:
pair<K, V> _Date;
RBTreeNode<K, V>* _left;
RBTreeNode<K, V>* _right;
RBTreeNode<K, V>* _parent;
Color _col;
RBTreeNode(const pair<K,V>& pir)
:_Date(pir)
,_left(nullptr)
,_right(nullptr)
,_parent(nullptr)
,_col(RED)
{}
};
template <class K, class V>
class RBTree
{
using Node = RBTreeNode<K, V>;
public:
void RotateR( Node* cur){
Node* SubL = cur->_left;
Node* SubLR =cur->_left->_right;
Node* pParent = cur->_parent;
if (cur == _root)
_root = SubL;
SubL->_right = cur;
cur->_parent = SubL;
if(SubLR)
SubLR->_parent = cur;
cur->_left = SubLR;
SubL->_parent=pParent;
if (pParent) {
if (pParent->_Date.first > SubL->_Date.first)
pParent->_left = SubL;
else if (pParent->_Date.first < SubL->_Date.first)
pParent->_right = SubL;
}
}
void RotateL( Node* cur) {
Node* SubR = cur->_right;
Node* SubRL = cur->_right->_left;
Node* pParent = cur->_parent;
if (cur == _root)
_root = SubR;
SubR->_left = cur;
cur->_parent = SubR;
cur->_right = SubRL;
if (SubRL)
SubRL->_parent = cur;
SubR->_parent = pParent;
if (pParent) {
if (pParent->_Date.first > SubR->_Date.first)
pParent->_left = SubR;
else if (pParent->_Date.first < SubR->_Date.first)
pParent->_right = SubR;
}
}
void RotateRL( Node* cur) {
Node* SubR = cur->_right;
Node* SubRL = cur->_right->_left;
RotateR(SubR);
RotateL(cur);
}
void RotateLR( Node* cur) {
Node* SubL = cur->_left;
Node* SubLR = cur->_left->_right;
RotateL(SubL);
RotateR(cur);
}
bool Insert(const pair<K, V> pir){
if (_root == nullptr){
_root = new Node(pir);
_root->_col = BLACK;
return true;
}
Node* cur = _root;
Node* parent = nullptr;
while (cur) {
if (cur->_Date.first > pir.first) {
parent = cur;
cur = cur->_left;
}
else if (cur->_Date.first < pir.first) {
parent = cur;
cur = cur->_right;
}
else
return false;
}
cur = new Node(pir);
cur->_col = RED;
cur->_parent = parent;
if (parent->_Date.first > cur->_Date.first)
parent->_left = cur;
else if (parent->_Date.first < cur->_Date.first)
parent->_right = cur;
while (parent && parent->_col == RED){
Node* grandpa = parent->_parent;
if (grandpa->_left == parent) {
Node* uncle = grandpa->_right;
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandpa->_col = RED;
cur = grandpa;
parent = grandpa->_parent;
}
else if (uncle == nullptr || uncle->_col == BLACK) {
if (parent->_left == cur) {
RotateR(grandpa);
grandpa->_col = RED;
parent->_col = BLACK;
}
else if (parent->_right == cur) {
RotateLR(grandpa);
grandpa->_col = RED;
cur->_col = BLACK;
}
break;
}
else assert(false);
}
else if (grandpa->_right == parent) {
Node* uncle = grandpa->_left;
if (uncle && uncle->_col == RED) {
parent->_col = BLACK;
uncle->_col = BLACK;
grandpa->_col = RED;
cur = grandpa;
parent = grandpa->_parent;
}
else if (uncle == nullptr || uncle->_col == BLACK) {
if (parent->_right == cur) {
RotateL(grandpa);
grandpa->_col = RED;
parent->_col = BLACK;
}
else if (parent->_left == cur) {
RotateRL(grandpa);
grandpa->_col = RED;
cur->_col = BLACK;
}
break;
}
else assert(false);
}
else assert(false);
}
_root->_col = BLACK;
return true;
}
void Inorder(){
inorder(_root);
cout << endl;
}
int Size() {
return size(_root);
}
int Height() {
return height(_root);
}
template <class K,class V>
Node* Find(const pair< K , V > pir) {
Node* cur = _root;
while (cur) {
if (cur->_Date.first > pir.first) {
cur = cur->_left;
}
else if (cur->_Date.first < pir.first) {
cur = cur->_right;
}
else
return cur;
}
return nullptr;
}
bool IsBalanceTree() {
if (_root->_col == RED) return false;
int Referance = 0;
Node* cur = _root;
while (cur) {
if (cur->_col == BLACK) Referance++;
cur = cur->_left;
}
return check(_root,0,Referance);
}
private:
bool check(const Node* cur,int BlackNum,int Referance) {
if (cur == nullptr && BlackNum != Referance) return false;
else return true;
if (cur->_col == RED) {
if (cur->_parent->_col == RED) {
cout << "NodeArea:" << cur->_parent->_Date.first << "->" << cur->_Date.first << "HaveContinuousRedNode" << endl;
return false;
}
}
else if (cur->_col == BLACK) BlackNum++;
return check(cur->_left, BlackNum, Referance) &&
check(cur->_right, BlackNum, Referance);
}
int size(const Node* root) {
if (root == nullptr) return 0;
return size(root->_left) + size(root->_right) + 1;
}
int height(const Node* root) {
if (root == nullptr) return 0;
int leftheight = height(root->_left);
int rightheight = height(root->_right);
return leftheight > rightheight ? leftheight + 1 : rightheight + 1;
}
void inorder(const Node* root) {
if (root == nullptr)
return;
inorder(root->_left);
cout << "key: " << root->_Date.first << "Value: " << root->_Date.second << endl;
inorder(root->_right);
}
private:
Node* _root = nullptr;
};
好了,本期内容到此结束,我是此方,我们下期再见。バイバイ!