Files
Data-Structure/BinaryTree/BalanceTree/Red-Black Tree/README.MD
2025-08-25 16:26:11 +08:00

264 lines
7.4 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

[TOC]
## 红黑树插入思路
好的,我们把 **红黑树插入的完整逻辑**整理一下,分步骤、分情况讲,保证清晰易懂:
---
## 🔹 红黑树插入逻辑
### 1⃣ 基本步骤
1. **普通 BST 插入**
* 按二叉搜索树的规则,把新节点 `x` 插入到合适位置。
* 新节点初始化为 **红色**(因为插入红色节点更容易维持黑高)。
2. **判断是否需要修复**
``` cpp
插入新节点 z (红色)
|
+-- 父是黑色 → OK直接插入
|
+-- 父是红色
|
+-- 叔叔是红色 → 父黑叔黑,祖红,上移到祖父,继续检查
|
+-- 叔叔是黑色
|
+-- z 在父的同向(左左或右右) → Case 3旋转 + 颜色交换
+-- z 在父的异向(左右或右左) → Case 2先旋转父再变为同向继续 Case 3
```
* 如果父节点是黑色 → 红黑性质不破坏,不需要 fix。
* 如果父节点是红色 → 违反“红色节点不能有红色子节点”,需要修复。
* 考虑叔叔是不是红的
* ->叔叔是红的就 一起变黑,祖父变红
* ->叔叔不是红的,考虑旋转+变色
---
### 2⃣ fixinsert 修复逻辑
设:
* `x` = 当前插入节点
* `p` = 父节点
* `u` = 叔叔节点(父节点的兄弟)
* `g` = 祖父节点
#### **情况 1叔叔节点是红色**
* **条件**`p` 是红色,`u` 是红色
* **处理**
1. 父节点 `p` 染黑
2. 叔叔节点 `u` 染黑
3. 祖父节点 `g` 染红
4. 将 `x` 上移到祖父节点 `g`,继续循环判断
* **原因**:通过重新染色,把红红冲突上移,保持黑高不变
---
#### **情况 2叔叔节点是黑色**
* **条件**`p` 是红色,`u` 是黑色
* **子情况**(根据 `x` 在父节点的左右位置):
1. **左-左LL**
* `x` 是 `p` 的左孩子,`p` 是 `g` 的左孩子
* **处理**
1. 父节点染黑,祖父染红
2. 右旋祖父
2. **左-右LR**
* `x` 是 `p` 的右孩子,`p` 是 `g` 的左孩子
* **处理**
1. 左旋父节点(将 LR 转化为 LL
2. 再执行 LL 情况的右旋 + 染色
3. **右-右RR**
* 对称 LL右孩子的右孩子插入
* 左旋祖父 + 调整颜色
4. **右-左RL**
* 对称 LR右孩子的左孩子插入
* 右旋父节点 → 变成 RR → 左旋祖父 + 调整颜色
---
### 3⃣ 循环终止
* 当父节点是黑色,或者到达根节点,循环结束
* 最后 **根节点必须染黑**
---
### 🔹 总结关键点
1. **新节点红色** → 保持黑高不变
2. **红红冲突** → 根据叔叔颜色分两类处理:
* 红色叔叔 → 上移染色
* 黑色叔叔 → 旋转 + 染色
3. **旋转 + 染色** → 只改变局部结构,保持整体 BST 和红黑性质
4. **根节点保持黑色** → 保证树的黑高一致
---
如果你需要,我可以帮你画一张 **红黑树插入示意图**,把 LL/LR/RR/RL 四种旋转场景和颜色变化可视化,看起来非常直观。
你想让我画吗?
## 红黑树删除思路
好,我给你梳理一下 **红黑树删除的整体思路**,只讲逻辑,不贴代码:
---
### 1⃣ 找到要删除的节点
* 首先按照普通二叉搜索树BST查找待删除节点 `z`。
* 如果节点不存在,就结束。
---
### 2⃣ 分情况处理节点
红黑树删除跟 BST 删除类似,分三种情况:
1. **节点没有子节点(叶子)**
* 直接把它从父节点断开。
* 如果节点是黑色,删除会破坏黑高,需要修复。
2. **节点只有一个孩子**
* 用唯一的孩子替代该节点挂到父节点上。
* 如果删除的是黑色节点,也可能破坏红黑性质,需要修复。
3. **节点有两个孩子**
* 找该节点的 **中序后继**(右子树最左的节点)替代它。
* 删除后继节点(后继最多有一个右孩子),这一步退化为情况 1 或 2。
* 然后把被删除节点的左右孩子接到后继上,继承颜色。
* 如果后继原本是黑色,也需要修复。
---
### 3⃣ 修复红黑树性质
* 删除黑色节点或用黑色节点替代时,会破坏 **黑高** 或 **红黑性质**。
* 修复核心思路:
1. **兄弟节点红色** → 旋转并调整颜色,使兄弟变黑。
2. **兄弟黑色,兄弟的孩子都是黑色** → 兄弟染红,把“双重黑”向上传递。
3. **兄弟黑色,有红色侄子** → 旋转 + 染色,直接消除双重黑。
* 修复循环一直执行,直到双重黑消除或者到达根节点。
---
### 4⃣ 特别注意
* 根节点始终要保持黑色。
* 删除过程尽量使用 **哨兵节点 `nul`** 替代空指针,避免每次判断 `nullptr`。
* 删除逻辑主要是 **BST 替换 + fixdelete 修复** 两步结合。
---
简单总结一句话就是:
**“先按 BST 删除节点,再根据被删节点颜色决定是否修复,用旋转和染色处理双重黑。”**
好,我们把 **红黑树删除后修复的核心步骤**细化讲解一下,把每种情况的逻辑和原因都拆开看。假设删除后出现的节点 `x` 带有 **“双重黑”double black**,这是修复的触发条件。
---
## 🔹 修复红黑树性质Double Black 处理)
### 情景概念
* 删除或替代节点是黑色 → 破坏了红黑树的黑高。
* 我们用 `x` 表示替代节点(可能是哨兵 `nul`)。
* `w` 表示 `x` 的兄弟节点。
---
### **情况 1兄弟节点红色**
**条件**`w->color == RED`
**处理逻辑**
1. 把兄弟染黑,父节点染红。
2. 对父节点做旋转(左旋或右旋,视 x 在左还是右)。
3. 转换成“兄弟是黑色”的情况,进入下一轮循环处理。
**原因**
* 红色兄弟意味着父节点必然是黑色(红黑性质),旋转后可以让黑高平衡,同时把问题转化为更容易处理的兄弟黑色情况。
---
### **情况 2兄弟是黑色且兄弟两个孩子都是黑色**
**条件**`w->color == BLACK && w->left->color == BLACK && w->right->color == BLACK`
**处理逻辑**
1. 把兄弟染红。
2. 把 x 的双重黑向父节点传递:`x = x->parent`,循环继续。
**原因**
* 黑高减少发生在 x 所在的路径上。
* 兄弟的两个子节点都是黑色 → 黑高没法补偿,只能把多余的黑色“向上递归”。
---
### **情况 3兄弟是黑色且有红色侄子**
**条件**`w->color == BLACK` 且至少有一个子节点红色
**处理逻辑**
1. 如果 x 是左孩子:
* case RL先对兄弟右旋让红色侄子转到右侧。
* case RR父亲左旋同时调整父亲、兄弟和红色侄子的颜色。
2. 对称地,如果 x 是右孩子:
* case LR先左旋兄弟
* case LL父亲右旋 + 染色
**原因**
* 红色侄子可以“补偿”被删除黑色节点造成的黑高缺失。
* 通过旋转和染色,双重黑立即消除,红黑树性质恢复。
---
### 🔹 循环结束条件
* 双重黑消除x 被重新染黑或者到达根节点)。
* 根节点必须保持黑色 → 最后再显式设置 `root->color = BLACK`。
---
💡 **直观理解**
1. **红兄弟** → 转化问题
2. **黑兄弟+黑侄子** → 往上递
3. **黑兄弟+红侄子** → 补偿黑高,立即解决
---
如果你想,我可以画一张 **删除修复流程图**,用节点颜色标注每一步的旋转和染色,直观展示三种情况。
你希望我画吗?