树与动态规划学习笔记
算法与数据结构核心知识点学习笔记
一、二维数组最短路径和(动态规划)
1.1 问题描述
给定一个包含非负整数的二维数组,要求从左上角 (0,0) 走到右下角 (m-1, n-1),每次只能向下或向右移动一步。目标是找到一条路径,使得路径上的数字总和最小。
示例矩阵:
| 1 | 3 | 1 |
| 1 | 5 | 1 |
| 4 | 2 | 1 |
最优路径: 1 → 3 → 1 → 1 → 1,总和为 7。
1.2 核心思路分析
这是一个经典的**动态规划(Dynamic Programming)**问题。
-
状态定义:dp[i][j] 表示从起点 (0,0) 走到 (i,j) 的最小路径和。
-
状态转移方程:
由于只能向下或向右走,到达 (i,j) 的前一步只能是 (i-1,j) 或 (i,j-1)。因此:
dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + arr[i][j]
-
边界条件:
- 起点:dp[0][0] = arr[0][0]
- 第一行:只能从左向右走,dp[0][j] = dp[0][j-1] + arr[0][j]
- 第一列:只能从上向下走,dp[i][0] = dp[i-1][0] + arr[i][0]
1.3 完整代码实现
package cn.wolfcode.test;
public class MinPathNum {
public static void main(String[] args) {
int[][] arr = {
{1, 3, 1},
{1, 5, 1},
{4, 2, 1}
};
System.out.println("最小路径和: " + minPath(arr));
}
/**
* 计算二维数组左上角到右下角的最小路径和
* @param arr 输入的二维数组
* @return 最小路径和
*/
public static int minPath(int[][] arr) {
if (arr == null || arr.length == 0 || arr[0].length == 0) {
return 0;
}
int m = arr.length; // 行数
int n = arr[0].length; // 列数
// 1. 创建 dp 数组
int[][] dp = new int[m][n];
// 2. 初始化起点
dp[0][0] = arr[0][0];
// 3. 初始化第一行(只能从左边来)
for (int j = 1; j < n; j++) {
dp[0][j] = dp[0][j - 1] + arr[0][j];
}
// 4. 初始化第一列(只能从上面来)
for (int i = 1; i < m; i++) {
dp[i][0] = dp[i - 1][0] + arr[i][0];
}
// 5. 填充其余位置(取上方或左方的最小值 + 当前值)
for (int i = 1; i < m; i++) {
for (int j = 1; j < n; j++) {
dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + arr[i][j];
}
}
// 6. 返回右下角的值
return dp[m - 1][n - 1];
}
}
1.4 复杂度分析
| 指标 | 复杂度 |
|---|---|
| 时间复杂度 | O(m × n) — 需要填充整个 dp 数组 |
| 空间复杂度 | O(m × n) — dp 数组占用 |
优化提示:空间可优化至 O(n),因为当前行只依赖上一行和当前行的左侧值,只需保留一维数组即可。
二、二叉树基础概念
2.1 满二叉树 (Full Binary Tree)
- 定义:如果一棵二叉树的高度为 h,则除第 h 层外,其余各层(1 到 h-1 层)的节点数都达到最大个数,且所有非叶子节点的度均为 2。
- 特征:节点总数 N = 2^h - 1。
- 性质:
- 第 i 层最多有 2^(i-1) 个节点
- 树高为 h 时总节点数最多为 2^h - 1
2.2 完全二叉树 (Complete Binary Tree)
- 定义:设二叉树高度为 h,则:
- 第 0 层到第 h-1 层必须是满二叉树。
- 第 h 层的节点必须从左向右连续排列,中间不能有空缺。
- 应用:堆(Heap)结构通常基于完全二叉树实现,适合用数组存储。
- 节点编号规律:对于编号为 i 的节点,其左孩子为 2i,右孩子为 2i+1,父节点为 ⌊i/2⌋。
2.3 二叉搜索树 (BST)
- 定义:一种特殊的二叉树,满足以下性质:
- 若左子树不为空,则左子树上所有节点的值 < 根节点的值。
- 若右子树不为空,则右子树上所有节点的值 > 根节点的值。
- 左右子树也分别为二叉搜索树。
- 中序遍历:结果是一个升序序列。例如:1 2 3 4 5 6。
- 缺点:在极端情况下(如插入有序数据),会退化为链表,查询复杂度退化为 O(N)。
三、AVL 树(自平衡二叉搜索树)
3.1 核心特性
AVL 树是为了解决 BST 退化问题而提出的严格平衡二叉搜索树。
- 平衡条件:任意节点的左右子树高度差的绝对值不超过 1。
- 优势:查询效率极高,时间复杂度稳定在 O(log N)。
- 劣势:为了维持严格平衡,插入和删除操作需要频繁旋转,增删效率相对较低。
- 中序遍历:结果仍为升序序列,例如:1 2 3 4 5 6。
3.2 删除操作详解
AVL 树删除节点后,需从删除点向上回溯检查平衡因子,必要时进行旋转调整。
| 场景 | 节点特征 | 处理方式 |
|---|---|---|
| 叶子节点 | 无左右孩子 | 直接删除,向上回溯检查平衡 |
| 单子节点 | 仅有左或右孩子 | 用该孩子节点直接代替被删节点 |
| 双子节点 | 同时有左右孩子 | 找到前驱(左子树最大值)或后继(右子树最小值)节点的值覆盖当前节点,然后转化为删除前驱/后继节点的问题 |
四、红黑树 (Red-Black Tree)
4.1 AVL 树 vs 红黑树
| 特性 | AVL 树 | 红黑树 |
|---|---|---|
| 平衡标准 | 严格平衡(高度差 ≤ 1) | 弱平衡(黑高相同,路径长度 ≤ 2倍) |
| 查询效率 | 极高 | 较高(略低于 AVL,但差距极小) |
| 增删效率 | 较低(频繁旋转) | 较高(旋转次数少,适合频繁修改) |
| 应用场景 | 读多写少(如内存中的查找表) | 读写频繁(如 Java TreeMap、HashMap) |
4.2 红黑树五大性质
- 颜色限制:每个节点非红即黑。
- 根节点:必须是黑色。
- 叶子节点:NIL 叶子节点必须是黑色。
- 红红不相邻:任意两个红色节点不能直接相连(父子不能同红)。
- 黑高一致:从任一节点到其所有叶子节点的路径上,黑色节点数量相同。
补充规则:新插入的节点默认染为红色,以减少对黑高性质的破坏。
4.3 红黑树删除操作规则
删除操作比插入复杂,核心在于处理黑色节点删除导致的黑高不平衡。
1. 物理删除策略
| 场景 | 处理方式 |
|---|---|
| 无孩子(叶子) | 红色节点直接删除,无影响;黑色节点删除后黑高减少,需进入修复流程 |
| 有 1 个孩子 | 用孩子节点代替,并将代替节点染黑 |
| 有 2 个孩子 | 用前驱或后继节点值覆盖,转化为删除前驱/后继(通常只有一个孩子或无孩子) |
2. 黑色节点修复逻辑
设被删节点为 N,兄弟节点为 S:
情况 A:兄弟节点 S 为红色
- 操作:父节点与 S 变色,对父节点进行旋转(LL/RR),转化为 S 为黑色的情况。
情况 B:兄弟节点 S 为黑色
| 子情况 | 操作 |
|---|---|
| S 有红色孩子(远侄为红) | 变色 + 旋转(LL/RR)。例如:S 的右孩子红,父左旋,S 变父色,父变黑,远侄变黑 |
| S 有红色孩子(近侄为红) | 先对 S 旋转(LR/RL),转化为远侄为红的情况,再按上述处理 |
| S 的孩子全黑 | 将 S 染红。若父节点原为红,则父变黑,结束;若父节点原为黑,则父节点视为"被删黑节点",继续向上递归修复 |
笔记案例:
- 节点 C 变 A,A 变黑,执行 LL 右旋
- 16黑 15红 9黑,执行左旋
- 旋转类型包括:LL、RR、RL、LR
五、核心知识点总结对照表
| 维度 | 二维数组最短路径 | 二叉搜索树 (BST) | AVL 树 | 红黑树 |
|---|---|---|---|---|
| 核心思想 | 动态规划 / 状态转移 | 左小右大排序 | 严格高度平衡 | 颜色与黑高约束 |
| 关键公式/性质 | dp[i][j] = min(up, left) + val | 中序遍历有序 | |h_L - h_R| ≤ 1 | 5大性质,红红不相邻 |
| 时间复杂度 | O(m × n) | 平均 O(log N) / 最坏 O(N) | O(log N) | O(log N) |
| 主要操作难点 | 边界初始化 | 退化问题 | 删除后的旋转调整 | 删除后的多重修复情况 |
| 适用场景 | 网格路径规划 | 基础排序/查找 | 内存高频查询 | 系统级集合/Map实现 |
| 笔记重点 | 第一行/列初始化不可漏 | 定义与遍历结果 | 删除三种情况分类 | 黑色节点删除修复逻辑 |
六、学习要点归纳
-
动态规划:二维数组最短路径问题的关键在于正确初始化第一行和第一列,状态转移方程取上方和左方的最小值加当前值。
-
二叉树三种类型:满二叉树(所有非叶子节点度为2)、完全二叉树(靠左连续)、二叉搜索树(左小右大)。
-
AVL树:严格平衡保证了 O(log N) 的查询效率,但删除操作需要回溯调整,增删效率较低。删除分三种情况:无孩子直接删、单子节点代替、双子节点用前驱/后继代替。
-
红黑树:弱平衡设计在查询效率和增删效率之间取得平衡,是 Java TreeMap、HashMap 等集合框架的核心数据结构。删除黑色节点时需要复杂的修复逻辑,包括变色和旋转(LL/RR/RL/LR 四种情况)。