绪论与复杂度分析

1. 课程定位

数据结构与算法(B) 是计算机基础课程之一,先修课程为编程语言 / 计算概论。课程强调:

  • 具备一定编程能力,能熟练运用 C 语言。
  • 培养抽象思维:把现实问题抽象成已有的数据模型和算法框架。
  • 只要抽象得好,理论上都可以转化为现有算法去解决。

Niklaus Wirth(Pascal 之父)的名言:

数据结构 + 算法 = 程序

虽然当今软件开发中这句话被质疑,但其核心思想依然成立:

  • 数据结构:研究信息在计算机中的表示问题。
  • 算法:研究信息在计算机中的处理问题。

2. 计算机问题求解过程

  1. 分析:弄清问题,用语言清楚表述。
  2. 设计:建立程序系统结构,重点是算法设计和数据结构设计。
  3. 编码:采用适当的程序设计语言编写可执行程序。
  4. 测试和维护:发现并排除前面阶段产生的错误,测试通过后投入运行。

3. 数据结构三要素(背诵重点)

要素 含义
逻辑结构 定义结构中基本元素之间的相互关系。用二元组 (D,R)(D, R) 表示,DD 是数据元素有限集合,RR 是 DD 上关系的有限集。
存储结构 给出基本元素的存储方式,包括元素的表示和关系的表示。
数据的运算 结构的行为特征,体现为在存储结构上的具体实现算法。

数据的存储结构

  1. 顺序存储结构
  2. 链式存储结构
  3. 索引存储结构
  4. 散列存储结构

4. 指针回顾

指针类型指向某一内存单元的地址。64 位机器上,一个指针占 8 个字节。

常见操作:

  • 分配地址、赋值(另一个指针的地址值、NULL)
  • 比较两个指针地址
  • 指针增减一个整数量(+1 跳过多少字节取决于指向对象大小)

关键符号:

  • &:取地址
  • *:声明指针 / 解引用
  • ->:访问结构体/对象成员
int a;
int *pa;
pa = &a;  // 指针 pa 指向 a

指针需要声明数据类型,因为编译器要知道一次取多少个字节。

5. 顺序检索算法示例

int SeqSearch(int element[], int n, int key, int *pos){
    int i;
    for (i = 0; i < n; i++){
        if (element[i] == key){
            *pos = i;
            return 1;
        }
    }
    *pos = i;   // i == n,表示未找到
    return 0;
}

数据结构的思路是:把共性的东西抽离出来,不应该因为 int 变成 float 就重写一遍。

6. 案例:多叉路口交通信号灯管理

问题:对车辆可能行驶方向分组,使同组内方向可安全同时行驶。

抽象过程:

  1. 每个可能行进方向是一个元素。
  2. 不能同时行驶的方向之间连一条边。
  3. 同组内不能连边 → 本质上是图着色问题。

这就是数学建模:把实际问题抽象为已有问题,再去找对应算法。

7. 算法复杂性分析

数据结构需要:

  • 一定空间存储每个数据项。
  • 一定时间执行单个基本操作。

评价算法好坏的两个维度:

  • 空间代价:不包括输入本身占用的空间,指算法额外需要的空间。
  • 时间代价:基本操作的执行次数 F(n)F(n)。

大 O 表示法

若存在正常数 cc 和 n0n_0,使得当 n≥n0n \ge n_0 时,T(n)≤c⋅f(n)T(n) \le c \cdot f(n),则称

T(n)=O(f(n)) T(n) = O(f(n))

大 O 只关注复杂性的量级,不关注系数。

常见量级关系(从大到小):

O(2n)≫O(nk)≫O(nlog⁡2n)≫O(n)≫O(log⁡2n)≫O(1) O(2^n) \gg O(n^k) \gg O(n \log_2 n) \gg O(n) \gg O(\log_2 n) \gg O(1)

运算规则:

O(n2)+O(n3)=O(n3) O(n^2) + O(n^3) = O(n^3)

最好、最坏、平均情况

算法基本操作执行次数往往与输入数据有关:

  • 最坏情况:输入数据集最坏时基本操作最多执行次数。
  • 平均情况:基本操作的平均执行次数。
  • 最好情况:最理想输入下的执行次数。

注意:"平均"需要明确概率测度。例如顺序查找若等概率分布,平均比较次数为 N/2N/2;若未说明分布,则不能直接这么算。

8. 示例:冒泡排序

void Bubble_Sort(int a[], int n){
    int i, j, change = 1;
    for (i = n - 1; i > 0 && change; --i){
        change = 0;
        for (j = 0; j < i; j++){
            if (a[j] > a[j + 1]){
                swap(&a[j], &a[j + 1]);
                change = 1;
            }
        }
    }
}
  • 额外空间:主要是 swap 中的临时变量。
  • 时间复杂度:最坏 O(n2)O(n^2),最好 O(n)O(n)(已有序时)。

9. 小结

  • 数据结构 = 逻辑结构 + 存储结构 + 运算。
  • 评价算法重点看时间复杂度和空间复杂度。
  • 大 O 表示法描述的是增长趋势,忽略低阶项和常数系数。