差别
这里会显示出您选择的修订版和当前版本之间的差别。
| 两侧同时换到之前的修订记录 前一修订版 后一修订版 | 前一修订版 | ||
| 数据结构:绪论 [2025/12/11 16:14] – [3. 动态规划 (Dynamic Programming)] 张叶安 | 数据结构:绪论 [2025/12/11 16:24] (当前版本) – [1. 穷举法 (Brute Force)] 张叶安 | ||
|---|---|---|---|
| 行 1: | 行 1: | ||
| ====== 绪论:数据结构与算法详解 (进阶版) ====== | ====== 绪论:数据结构与算法详解 (进阶版) ====== | ||
| - | 程序设计的核心公式(N.沃思): | + | **"程序设计 = 数据结构 + 算法"** —— 尼克劳斯·沃思 |
| - | > **程序 | + | |
| - | | + | 如果把编程比作**烹饪**: |
| - | * **算法**:是**动态**的。就像图书管理员找书的流程(是从左往右一本本找,还是先查索引再找)。 | + | * **数据结构 |
| + | * | ||
| + | |||
| + | 本教程旨在通过可视化的方式,深入剖析计算机科学的这两大基石。 | ||
| ===== 第一部分:数据结构 (Data Structure) ===== | ===== 第一部分:数据结构 (Data Structure) ===== | ||
| + | |||
| + | 数据结构不仅仅是存储数据,更是为了**高效地**检索和操作数据。 | ||
| ==== 1. 核心概念辨析 ==== | ==== 1. 核心概念辨析 ==== | ||
| - | 我们通过一个**“学生信息管理系统”**的例子来理解: | + | 在深入具体结构前,我们需要厘清以下术语的层级关系: |
| - | * **数据 (Data)**:计算机处理的符号总称。 | + | ^ 术语 ^ 定义 ^ 举例 (学生管理系统) ^ |
| - | | + | | **数据 (Data)** |
| - | | + | | **数据元素 (Data Element)** |
| - | | + | | **数据项 (Data Item)** |
| + | | **数据对象 (Data Object)** | ||
| - | ==== 2. 逻辑结构可视化 | + | ==== 2. 逻辑结构:数据之间的“思想关系” |
| - | 逻辑结构描述的是数据在**思想上**的关联。以下是四种基本结构的交互式演示: | + | 逻辑结构是指数据元素之间的相互关系,它与数据在计算机中的存储位置无关。我们通常将其分为四类。 |
| < | < | ||
| 行 37: | 行 42: | ||
| margin: 0 auto 20px auto; | margin: 0 auto 20px auto; | ||
| } | } | ||
| - | |||
| - | /* 网格布局 */ | ||
| .ds-grid { | .ds-grid { | ||
| display: grid; | display: grid; | ||
| - | grid-template-columns: | + | grid-template-columns: |
| gap: 25px; | gap: 25px; | ||
| } | } | ||
| - | |||
| - | /* 卡片样式 */ | ||
| .ds-card { | .ds-card { | ||
| background: white; | background: white; | ||
| 行 58: | 行 59: | ||
| align-items: | align-items: | ||
| } | } | ||
| - | |||
| .ds-card: | .ds-card: | ||
| transform: translateY(-7px); | transform: translateY(-7px); | ||
| 行 64: | 行 64: | ||
| border-bottom-color: | border-bottom-color: | ||
| } | } | ||
| - | |||
| - | /* 图标区域 */ | ||
| .ds-icon-box { | .ds-icon-box { | ||
| - | height: 110px; | + | height: 110px; |
| width: 100%; | width: 100%; | ||
| display: flex; | display: flex; | ||
| 行 76: | 行 74: | ||
| border-radius: | border-radius: | ||
| } | } | ||
| - | |||
| - | /* SVG 通用样式 */ | ||
| .ds-svg { | .ds-svg { | ||
| - | width: 140px; | + | width: 140px; |
| height: 90px; | height: 90px; | ||
| overflow: visible; | overflow: visible; | ||
| } | } | ||
| - | | + | .ds-shape { fill: #3498db; } |
| - | /* 节点样式 */ | + | .ds-shape-2 { fill: #e74c3c; } |
| - | | + | .ds-shape-3 { fill: #f1c40f; } |
| - | .ds-shape-2 { fill: #e74c3c; } /* 红色 */ | + | .ds-shape-4 { fill: #2ecc71; } |
| - | .ds-shape-3 { fill: #f1c40f; } /* 黄色 */ | + | |
| - | .ds-shape-4 { fill: #2ecc71; } /* 绿色 */ | + | |
| - | + | ||
| - | /* 线条样式 */ | + | |
| .ds-line { stroke: #34495e; stroke-width: | .ds-line { stroke: #34495e; stroke-width: | ||
| .ds-dashed { stroke: #bdc3c7; stroke-width: | .ds-dashed { stroke: #bdc3c7; stroke-width: | ||
| - | |||
| - | /* 集合动画:不同步的浮动 */ | ||
| .anim-float-1 { animation: float 3s infinite ease-in-out; | .anim-float-1 { animation: float 3s infinite ease-in-out; | ||
| .anim-float-2 { animation: float 4s infinite ease-in-out 0.5s; } | .anim-float-2 { animation: float 4s infinite ease-in-out 0.5s; } | ||
| .anim-float-3 { animation: float 3.5s infinite ease-in-out 1.2s; } | .anim-float-3 { animation: float 3.5s infinite ease-in-out 1.2s; } | ||
| .anim-float-4 { animation: float 4.5s infinite ease-in-out 2s; } | .anim-float-4 { animation: float 4.5s infinite ease-in-out 2s; } | ||
| - | | ||
| @keyframes float { | @keyframes float { | ||
| 0%, 100% { transform: translateY(0); | 0%, 100% { transform: translateY(0); | ||
| 50% { transform: translateY(-4px); | 50% { transform: translateY(-4px); | ||
| } | } | ||
| - | |||
| - | /* 文字排版 */ | ||
| h4 { margin: 0 0 8px 0; color: #2c3e50; font-size: 1.1em; font-weight: | h4 { margin: 0 0 8px 0; color: #2c3e50; font-size: 1.1em; font-weight: | ||
| p.desc { font-size: 0.9em; color: #7f8c8d; margin: 0; line-height: | p.desc { font-size: 0.9em; color: #7f8c8d; margin: 0; line-height: | ||
| p.example { font-size: 0.8em; color: #95a5a6; margin-top: 5px; font-style: italic; } | p.example { font-size: 0.8em; color: #95a5a6; margin-top: 5px; font-style: italic; } | ||
| - | |||
| </ | </ | ||
| </ | </ | ||
| < | < | ||
| - | |||
| <div class=" | <div class=" | ||
| <div class=" | <div class=" | ||
| - | | + | <!-- 1. 集合结构 --> |
| - | | + | |
| <div class=" | <div class=" | ||
| <div class=" | <div class=" | ||
| <svg class=" | <svg class=" | ||
| - | <!-- 集合边界 --> | ||
| <ellipse cx=" | <ellipse cx=" | ||
| - | <!-- 元素:更多数量,更杂乱 --> | ||
| <circle cx=" | <circle cx=" | ||
| <circle cx=" | <circle cx=" | ||
| 行 136: | 行 118: | ||
| <p class=" | <p class=" | ||
| </ | </ | ||
| - | + | | |
| - | | + | |
| <div class=" | <div class=" | ||
| <div class=" | <div class=" | ||
| <svg class=" | <svg class=" | ||
| - | <!-- 连接线 --> | ||
| <line x1=" | <line x1=" | ||
| - | <!-- 节点:5个节点,体现序列 --> | ||
| <circle cx=" | <circle cx=" | ||
| <circle cx=" | <circle cx=" | ||
| 行 155: | 行 134: | ||
| <p class=" | <p class=" | ||
| </ | </ | ||
| - | + | | |
| - | | + | |
| <div class=" | <div class=" | ||
| <div class=" | <div class=" | ||
| <svg class=" | <svg class=" | ||
| - | <!-- 连线:根到第二层 --> | ||
| <line x1=" | <line x1=" | ||
| <line x1=" | <line x1=" | ||
| - | <!-- 连线:第二层到第三层 --> | ||
| <line x1=" | <line x1=" | ||
| <line x1=" | <line x1=" | ||
| <line x1=" | <line x1=" | ||
| <line x1=" | <line x1=" | ||
| - | + | | |
| - | <!-- 节点:3层结构 --> | + | <circle cx=" |
| - | | + | <circle cx=" |
| - | | + | <circle cx=" |
| - | <circle cx=" | + | <circle cx=" |
| - | <circle cx=" | + | <circle cx=" |
| - | | + | <circle cx=" |
| - | <circle cx=" | + | |
| - | <circle cx=" | + | |
| - | <circle cx=" | + | |
| - | <circle cx=" | + | |
| </ | </ | ||
| </ | </ | ||
| 行 185: | 行 157: | ||
| <p class=" | <p class=" | ||
| </ | </ | ||
| - | + | | |
| - | | + | |
| <div class=" | <div class=" | ||
| <div class=" | <div class=" | ||
| <svg class=" | <svg class=" | ||
| - | <!-- 复杂的网状连线 --> | ||
| - | <!-- 外圈连接 --> | ||
| <line x1=" | <line x1=" | ||
| <line x1=" | <line x1=" | ||
| 行 197: | 行 166: | ||
| <line x1=" | <line x1=" | ||
| <line x1=" | <line x1=" | ||
| - | | ||
| - | <!-- 内部交叉连接 (制造复杂感) --> | ||
| <line x1=" | <line x1=" | ||
| <line x1=" | <line x1=" | ||
| <line x1=" | <line x1=" | ||
| - | + | | |
| - | <!-- 节点:五边形布局 --> | + | <circle cx=" |
| - | | + | <circle cx=" |
| - | <circle cx=" | + | <circle cx=" |
| - | <circle cx=" | + | <circle cx=" |
| - | <circle cx=" | + | |
| - | <circle cx=" | + | |
| </ | </ | ||
| </ | </ | ||
| 行 215: | 行 180: | ||
| <p class=" | <p class=" | ||
| </ | </ | ||
| - | |||
| </ | </ | ||
| </ | </ | ||
| - | |||
| </ | </ | ||
| </ | </ | ||
| + | === 2.1 集合 (Set) === | ||
| + | * | ||
| + | * | ||
| + | |||
| + | === 2.2 线性结构 (Linear) === | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | * **栈 (Stack)**:后进先出 (LIFO),如弹夹。 | ||
| + | * | ||
| - | ==== 3. 存储结构:数据在“内存”中的样子 ==== | + | === 2.3 树状结构 |
| + | * | ||
| + | * | ||
| - | 这是逻辑结构的物理实现。 | + | === 2.4 图形结构 |
| + | * | ||
| + | * | ||
| - | === 3.1 顺序存储 | + | ==== 3. 存储结构:数据在“内存”中的物理形态 ==== |
| - | ^ 特性 ^ 顺序存储 | + | 逻辑结构是“想出来的”,存储结构是“写在内存条上的”。 |
| - | | **物理位置** | 必须相邻 | 可以散落在内存各处 | | + | |
| - | | **优点** | **查找快** (直接算地址),存储密度大 | **增删快** (改指针即可),不需预分配 | | + | |
| - | | **缺点** | 插入删除需移动元素 | 查找慢 (必须从头找),指针占空间 | | + | |
| - | === 3.2 进阶存储方式 | + | === 3.1 顺序存储 |
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| - | * **索引存储**:建立一张“目录表”。先查目录找到地址,再找内容。(类似字典的拼音索引)。 | + | === 3.2 链式存储 (Linked Storage) === |
| - | * **散列存储 (Hash)**:**速度之王**。 | + | * **原理**:逻辑上相邻的元素在物理上可以**不相邻**。通过“指针” (Pointer) 链接。 |
| - | * // | + | * |
| - | * //例子//:存 `105`,公式 `key % 10`,直接扔进 `5` 号房间。查找时直接去 `5` 号拿。 | + | * |
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | |||
| + | === 3.3 索引存储与散列存储 === | ||
| + | * | ||
| + | * | ||
| + | * 不通过比较,而是通过公式(哈希函数)直接计算出存储地址。 | ||
| + | * **冲突 (Collision)**:当不同的数据算出了相同的地址时,需要特殊处理(如拉链法)。 | ||
| ===== 第二部分:算法 (Algorithm) ===== | ===== 第二部分:算法 (Algorithm) ===== | ||
| - | ==== 1. 算法效率度量 | + | 算法是解决特定问题求解步骤的描述。一个好的算法应该具备:**正确性、可读性、健壮性、高效率**。 |
| + | |||
| + | ==== 1. 算法效率度量:大O记号 | ||
| - | 我们用 **大O记号** 表示随着数据量 $n$ 增大,耗时增长的趋势。 | + | 我们不计算具体的秒数(因为这取决于机器性能),而是计算**操作步骤数量随数据量 $n$ 的增长趋势**。 |
| < | < | ||
| 行 293: | 行 289: | ||
| transition: width 1s ease-out; | transition: width 1s ease-out; | ||
| } | } | ||
| - | /* 颜色定义 */ | ||
| .bg-green { background-color: | .bg-green { background-color: | ||
| .bg-blue { background-color: | .bg-blue { background-color: | ||
| 行 325: | 行 320: | ||
| </ | </ | ||
| + | === 1.1 常见复杂度详解 === | ||
| - | * **$O(1)$ 常数阶**:无论数据多少,耗时不变。 | + | * |
| - | * **$O(n)$ 线性阶**:数据翻倍,耗时翻倍。 | + | <code c> |
| - | * **$O(n^2)$ 平方阶**:数据翻倍,耗时变 4 倍(双层循环)。 | + | int n = 10000; |
| - | * **$O(\log_2 n)$ 对数阶**:非常快(二分查找)。 | + | x = n + 1; // 无论n多大,这行只执行一次 |
| + | </ | ||
| + | * | ||
| + | <code c> | ||
| + | for(int i=0; i<n; i++) { ... } | ||
| + | </ | ||
| + | * | ||
| + | <code c> | ||
| + | for(int i=0; i<n; i++) { | ||
| + | for(int j=0; j<n; j++) { ... } | ||
| + | } | ||
| + | </ | ||
| + | * | ||
| + | <code c> | ||
| + | while(n > 1) { | ||
| + | n = n / 2; // 1024 -> 512 -> 256 ... 10次就到了 | ||
| + | } | ||
| + | </ | ||
| ===== 第三部分:五大常用算法思想详解 ===== | ===== 第三部分:五大常用算法思想详解 ===== | ||
| + | |||
| + | 这是算法的灵魂,掌握这些思想比背诵代码更重要。 | ||
| ==== 1. 穷举法 (Brute Force) ==== | ==== 1. 穷举法 (Brute Force) ==== | ||
| - | * **核心**:暴力破解,试遍所有可能。 | ||
| - | * **优化**:**剪枝 (Pruning)**。如果在搜索过程中发现当前路径不可能成功,就直接放弃,不再往下试(剪掉树枝)。 | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| + | * | ||
| ==== 2. 分治法 (Divide and Conquer) ==== | ==== 2. 分治法 (Divide and Conquer) ==== | ||
| - | * **核心口诀**:**分而治之,各个击破**。 | + | * |
| - | * **三个步骤**: | + | * |
| - | - 1. **分解 (Divide)**:将大问题分解为若干个规模较小的同类子问题。 | + | * |
| - | - 2. **解决 (Conquer)**:递归地解决这些子问题。 | + | |
| - | - 3. **合并 (Combine)**:将子问题的解合并为原问题的解。 | + | |
| - | * **经典案例**:归并排序、快速排序、二分查找。 | + | |
| < | < | ||
| 行 374: | 行 389: | ||
| < | < | ||
| <div class=" | <div class=" | ||
| - | <!-- Level 1 --> | ||
| <div class=" | <div class=" | ||
| <div class=" | <div class=" | ||
| - | <!-- Level 2 --> | ||
| <div> | <div> | ||
| <div class=" | <div class=" | ||
| 行 383: | 行 396: | ||
| </ | </ | ||
| <div class=" | <div class=" | ||
| - | <!-- Level 3 --> | ||
| <div> | <div> | ||
| <div class=" | <div class=" | ||
| 行 394: | 行 406: | ||
| </ | </ | ||
| + | ==== 3. 动态规划 (Dynamic Programming, | ||
| - | ==== 3. 动态规划 (Dynamic Programming) ==== | + | |
| - | + | * | |
| - | | + | * |
| - | * **原理**:把大问题拆成小问题,但**每个小问题的结果都存起来**(通常存在数组或表中)。下次需要时,直接查表,不用重新算。 | + | * |
| - | * **适用场景**:求最值(最长路径、最大价值)、背包问题。 | + | * 求 $F(4)$ 时需要算 |
| - | * **与分治法的区别**:分治法的子问题是独立的;DP 的子问题是有重叠的。 | + | * |
| + | * | ||
| < | < | ||
| 行 463: | 行 477: | ||
| </ | </ | ||
| </ | </ | ||
| - | |||
| ==== 4. 贪心算法 (Greedy) ==== | ==== 4. 贪心算法 (Greedy) ==== | ||
| - | * **核心**:**目光短浅,只看眼前**。 | + | * |
| - | * **策略**:在每一步选择中都采取在当前状态下最好(即最有利)的选择,希望最后堆出来的结果也是全局最好的。 | + | * |
| - | * **局限**:不能保证一定是全局最优解(比如走迷宫,一直往目标方向走可能会走进死胡同)。 | + | * |
| - | * **经典案例**:找零钱问题(优先用大面额)、霍夫曼编码。 | + | * |
| + | * | ||
| + | * | ||
| + | * 66 < 100,跳过。 | ||
| + | * 66 >= 50,**给一张 50**。剩 16。 | ||
| + | | ||
| + | | ||
| + | * 6 >= 5,**给一张 5**。剩 1。 | ||
| + | * **给一张 1**。结束。 | ||
| + | * | ||
| ==== 5. 回溯法 (Backtracking) ==== | ==== 5. 回溯法 (Backtracking) ==== | ||
| - | * **核心**:**不撞南墙不回头**。 | + | * |
| - | * **策略**:一条路走到黑,发现走不通了,就退回到上一个路口(回溯),换一条路继续走。 | + | * |
| - | * **本质**:深度优先搜索 | + | * |
| - | * **经典案例**:八皇后问题、走迷宫、数独。 | + | * |
| < | < | ||
| 行 498: | 行 520: | ||
| transition: all 0.5s; | transition: all 0.5s; | ||
| } | } | ||
| - | | + | .p1 { top: 50px; left: 10px; width: 40px; height: 10px; } |
| - | | + | .p2 { top: 20px; left: 40px; width: 10px; height: 40px; } |
| - | .p2 { top: 20px; left: 40px; width: 10px; height: 40px; } /* 向上尝试 */ | + | .p3 { top: 20px; left: 40px; width: 40px; height: 10px; background: #f44336; } |
| - | .p3 { top: 20px; left: 40px; width: 40px; height: 10px; background: #f44336; } /* 死路 */ | + | .p4 { top: 50px; left: 40px; width: 60px; height: 10px; } |
| - | | + | .p5 { top: 50px; left: 90px; width: 10px; height: 40px; } |
| - | .p4 { top: 50px; left: 40px; width: 60px; height: 10px; } /* 回溯后向右 */ | + | .p6 { top: 80px; left: 90px; width: 60px; height: 10px; background: #4caf50; } |
| - | .p5 { top: 50px; left: 90px; width: 10px; height: 40px; } /* 向下 */ | + | |
| - | .p6 { top: 80px; left: 90px; width: 60px; height: 10px; background: #4caf50; } /* 通路 */ | + | |
| - | | + | |
| .bt-label { | .bt-label { | ||
| position: absolute; | position: absolute; | ||
| 行 522: | 行 541: | ||
| <div class=" | <div class=" | ||
| <div class=" | <div class=" | ||
| - | | ||
| <div class=" | <div class=" | ||
| <div class=" | <div class=" | ||
| 行 532: | 行 550: | ||
| </ | </ | ||
| </ | </ | ||
| - | </ | ||
| - | |||
| - | ===== 总结 ===== | ||
| - | |||
| - | 学习数据结构与算法,不是为了死记硬背代码,而是为了培养**“计算思维”**: | ||
| - | - 遇到复杂问题 -> **分治** | + | ===== 总结:如何选择算法? ===== |
| - | - 遇到重复计算 -> **动态规划** | + | |
| - | - 需要快速试错 -> **回溯** | + | |
| - | - 需要资源最优 -> **贪心** | + | |
| - | 希望这份可视化的笔记能帮助你建立起知识框架! | + | | 场景特点 | 推荐策略 | 核心思想 | |
| + | | 问题可拆分,子问题独立 | **分治法** | 大事化小 | | ||
| + | | 问题可拆分,子问题重叠 | **动态规划** | 拒绝健忘,查表 | | ||
| + | | 需要快速找到一个可行解 | **贪心算法** | 目光短浅,先拿再说 | | ||
| + | | 需要遍历所有解或路径 | **回溯法** | 不撞南墙不回头 | | ||
| + | | 没有任何规律 | **穷举法** | 暴力出奇迹 | | ||