计算机科学的核心主题是对算法的研究。什么是算法?如何表示它、发现它?它跑得多快、又如何证明它正确?——本章一次讲透。
六个小节回答六个问题;每节末尾有「本节小结」与课堂练习(共 12 题),参考答案在页面底部。
| 小节 | 核心问题 | 关键概念 |
|---|---|---|
| 5.1 算法的概念 | 什么样的步骤序列才配称为算法? | 有序 · 无歧义 · 可执行 · 可终止 |
| 5.2 算法的表示 | 如何精确地把算法写下来给别人看? | 原语 · 伪代码 · 函数与参数 |
| 5.3 算法的发现 | 算法是怎样被“想”出来的? | 波利亚四阶段 · 逐步精化 |
| 5.4 迭代结构 | 如何用循环实现搜索与排序? | 顺序搜索 · 循环控制 · 插入排序 |
| 5.5 递归结构 | 如何让函数调用自己来解题? | 二分搜索 · 激活 · 递归控制 |
| 5.6 效率和正确性 | 算法有多快?如何确保它正确? | 大Θ符号 · 断言 · 循环不变式 |
算法 algorithm:定义一个可终止过程的一组无歧义的、可执行的步骤的有序集合。
算法的正式定义:一组无歧义、可执行的步骤的有序集合,且定义的过程可终止——四个条件缺一不可。
算法是抽象的,与表示方式无关;程序是算法的表示,进程是执行算法的活动——算法把程序和进程联系在一起。
用定义明确的构建块——原语(primitive)——来构造算法的表示,消除自然语言的歧义。
伪代码 pseudocode:算法开发中非正式表达想法的符号系统——比自然语言准确,比程序设计语言易读。
原语 = 语法(符号表示)+ 语义(含义);程序设计语言 = 原语集合 + 组合规则。
伪代码三种结构:赋值、选择 if-else、迭代 while;冒号与缩进限定子结构边界。
函数 + 参数让算法成为可复用的抽象工具;算法可以组合形成新算法。
程序开发 = 发现潜在的算法 + 以程序方式表示算法——“发现”往往是更具挑战性的一步。
波利亚四阶段:理解问题 → 设计计划 → 完成计划 → 评估;阶段供回顾参照,而非死板遵循。
迈出第一步的方法:反向求解、借助相关的已解问题、逐步精化(自顶向下),并与自底向上互补。
算法不是现成的知识,而是需要发现的未知、需要试错的技术、需要创造的艺术。
迭代结构 iterative structure:一组指令以循环(loop)方式重复执行,重复执行的指令组称为循环体(body)。
| 活动 | 职责 |
|---|---|
| 初始化 initialization | 建立初始状态,该状态会朝着终止条件被修改 |
| 测试 test | 比较当前状态与终止条件,若相等则终止重复 |
| 修改 modification | 改变状态,使之移向终止条件 |
循环控制 = 初始化 + 测试 + 修改,三者必须保证终止条件最终出现,否则死循环。
顺序搜索逐个比对(线性搜索),适合短列表;for 循环是遍历列表的高级抽象。
插入排序反复把主元插入已排好序的前缀,是嵌套循环的典型例子。
递归 recursion:函数直接或间接地调用自身——把问题分解为更小的同类子问题,直到最简单情形时停止并返回结果。
| 要素 | 在二分搜索中的体现 |
|---|---|
| 初始化 | 隐式开始:直接给出原始列表和目标值 |
| 修改 | 把任务修改为在更小的列表中搜索 |
| 终止条件 | 找到目标值,或任务缩小到搜索空列表 |
递归:函数调用自身,把问题分解为更小的同类子问题,直到最简单情形;每次调用产生一次“激活”。
二分搜索每次比较中间条目、排除一半,前提是列表有序。
递归控制与循环控制同样需要初始化、修改和终止条件,缺一不可。
效率 efficiency:资源的利用程度——高效与低效算法之差,往往是“实用解决方案”与“不实用解决方案”之别。
| 算法 | 时间-输入图的形状 | 大Θ分类 | 输入规模翻倍时 |
|---|---|---|---|
| 插入排序 insertion sort | 抛物线(二次表达式) | Θ(n²) | 耗时约变为 4 倍 |
| 二分搜索 binary search | 对数曲线(对数表达式) | Θ(log₂n) | 只多一次比较 |
大Θ符号按增长曲线的形状分类算法:插入排序 Θ(n²),二分搜索 Θ(log₂n);高效算法让更大规模的实例可解。
正确性用前置条件、断言、后置条件与循环不变式作形式化证明,而非依赖测试;软件验证仍是活跃的研究领域。
逐题一句话解析;错题请回到对应小节复习。
| 小节 | 题号 | 答案 | 一句话解析 |
|---|---|---|---|
| 5.1 | Q1 | B | 算法必须定义一个可终止的过程——永不结束的过程不是算法。 |
| 5.1 | Q2 | C | 程序是算法的表示,进程是执行程序(算法)的活动。 |
| 5.2 | Q3 | C | 语义 = 含义;符号表示是“语法”。 |
| 5.2 | Q4 | A | 定序、选择、迭代三种构建块即可构造任何算法。 |
| 5.3 | Q5 | B | 先理解问题,再谈设计计划。 |
| 5.3 | Q6 | A | 逐步精化从一般到特殊,是自顶向下方法。 |
| 5.4 | Q7 | C | 顺序搜索又称线性搜索,按出现顺序逐项比对。 |
| 5.4 | Q8 | D | 初始化、测试、修改——三者共同保证终止条件出现。 |
| 5.5 | Q9 | B | 二分搜索依赖有序性:比较中间项后知道该去哪一半。 |
| 5.5 | Q10 | C | 递归函数的每个“副本”是一次激活,任一时刻只有一个活跃。 |
| 5.6 | Q11 | C | 插入排序最差情况图呈抛物线 → Θ(n²)。 |
| 5.6 | Q12 | D | 开始时满足的是前置条件;末尾要推出的是后置条件。 |
重点名词双语对照 + 一句话释义;建议结课时自查一遍。
| 术语 | English | 释义 |
|---|---|---|
| 算法 | algorithm | 可终止的精确步骤集合 |
| 并行算法 | parallel algorithm | 多执行流分支再接合 |
| 程序 | program | 算法的表示 |
| 进程 | process | 执行程序的活动 |
| 原语 | primitive | 定义明确的构建块 |
| 语法 | syntax | 原语的符号表示 |
| 语义 | semantics | 原语的含义 |
| 程序设计语言 | programming language | 原语集合 + 组合规则 |
| 伪代码 | pseudocode | 非正式的算法表达法 |
| 赋值语句 | assignment statement | name = expression |
| 迭代 | iteration | 重复执行直到满足条件 |
| 函数 | function | 命名的代码块(def) |
| 参数 | parameter | 函数的类属名输入 |
| 术语 | English | 释义 |
|---|---|---|
| 逐步精化 | stepwise refinement | 逐级分解为子问题 |
| 自顶向下 | top-down methodology | 从一般到特殊 |
| 自底向上 | bottom-up methodology | 从特殊到一般 |
| 循环 | loop | 迭代结构,重复循环体 |
| 终止条件 | termination condition | 使循环停止的条件 |
| 顺序搜索 | sequential search | 逐项查找(线性搜索) |
| 插入排序 | insertion sort | 把主元插入已排序前缀 |
| 主元 | pivot | 被移动的基准条目 |
| 递归 | recursion | 函数调用自身 |
| 二分搜索 | binary search | 有序列表逐次减半 |
| 激活 | activation | 递归函数的一次“副本” |
| 大Θ符号 | big-theta notation | 按效率曲线形状分类 |
| 循环不变式 | loop invariant | 每次终止测试都成立的断言 |