1. 算法、规范与正确性
在这一模块中,我们将介绍算法的定义和分析方法,并讲解一些经典算法的 C 语言实现。
数学基础要求
本模块假定读者有一定的数学水平:基本的算术 + 集合论 + 概率论 + 矩阵。
《算法导论》附录 A~D 中有对这部分数学的简明扼要的讲解。
1. 基本概念
1.1 定义
算法是为了解决某类问题而规定的一个有限长的操作序列。
1.2 特征
- 有穷性:一个算法必须总是在执行有穷步后结束,且每一步都必须在有穷时间内完成。
- 确定性:对每种情况下所应执行的操作,在算法中都有确切的规定,使算法的执行者或阅读者都能明确其含义及如何执行,不会产生二义性。
- 可行性:算法中的所有操作都可以通过已经实现的基本操作运算执行有限次来实现。
- 输入:一个算法有零个或多个输入。当用函数描述算法时,输入往往是通过形参表示的,在它们被调用时,从主调函数获得输入值。
- 输出:一个算法有一个或多个输出,它们是算法进行信息加工后得到的结果。
1.3 一个“公式”
1.4 算法的表示
- 自然语言
- 流程图和 N-S 图
- 伪代码(《算法导论》中采用这种方法)
2. 从问题描述到实现
编写代码之前,应先明确三点:输入是什么,输出是什么,算法由哪些步骤组成、为什么在有限步后结束。明确这三点之后,C 语言实现中的每个循环和分支都能对应到描述中的某一步,程序出错时也容易判断是算法有误还是实现有误。
3. 正确性与复杂度要同时给出
给出一个算法时,应同时回答两个问题:它为什么一定得到正确结果;它的时间和空间代价随输入规模如何增长。缺少正确性论证,就无法确认算法在边界输入上的行为;缺少复杂度分析,就无法判断输入规模增大后算法是否仍然可用。后续章节讨论每个算法时都会回答这两个问题。
4. 示例:线性搜索
下面的函数在数组中查找目标值第一次出现的位置,若不存在则返回 -1。
#include <stdio.h>
static int linear_search(const int *a, int n, int target) {
for (int i = 0; i < n; ++i) {
if (a[i] == target) {
return i;
}
}
return -1;
}
int main(void) {
int a[] = {8, 3, 2, 7, 3};
int n = (int)(sizeof(a) / sizeof(a[0]));
printf("index(7) = %d\n", linear_search(a, n, 7));
printf("index(9) = %d\n", linear_search(a, n, 9));
return 0;
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
运行结果:
index(7) = 3 index(9) = -1
线性搜索在最坏情况下比较
5. 习题
某些算法实际上是数学题目:首先要用数学技巧求得解的简单表达形式,再编写简单的程序直接输出结果。这道题带有一定脑筋急转弯性质,解答这道题对编程技巧和学习算法没有太大帮助。
(1) [2.2] 平面上有
(2) [3.2] 甲乙两人在玩抽硬币游戏。