算法与复杂度:练习题及解析

免费的算法与复杂度选择练习题,附提示和解析,分三个难度:关键术语与事实, 概念应用, 案例与分析。

免费练习题简单

当 n 很大时,哪个复杂度类增长最快?

下一题 →
免费练习算法与复杂度立即练习算法与复杂度每天 5 道免费题目,无需绑定银行卡。

练习内容

  1. 简单关键术语与事实
  2. 中等概念应用
  3. 困难案例与分析

从零开始学习算法与复杂度

1复杂度衡量什么

算法是解决一个问题的有限的、精确的步骤列表,例如给一个列表排序,或在电话簿中查找一个名字。

两个算法可以解决同一个问题,但所用的时间可能相差很大。复杂度描述的是输入变大时工作量如何增长。

输入的规模称为 n:要排序的项目数、列表的长度、图中的节点数。

想一想在一本 1000 页的电话簿里找一个名字。读每一页最多需要 1000 步。从中间翻开并每次减半,大约只需要 10 步。

时间复杂度计算步骤数。空间复杂度计算额外的内存。两者都写成 n 的函数。

通常看最坏情况:规模为 n 的所有输入中工作量最大的情况。有时题目问的是平均情况或最好情况。

自测 一个工作量按 n² 增长的算法,在 n = 1000 时需要 1 秒。n = 2000 时大约需要多长时间?
  1. 1 秒
  2. 2 秒
  3. 4 秒
  4. 8 秒

答案: 4 秒. n 加倍使 n² 乘以 2² = 4,所以时间从约 1 秒变为约 4 秒。2 秒是线性增长的答案,8 秒是 n³ 的答案。

2大 O 和增长类别

大 O 记号只保留对大的 n 重要的部分。你要舍去常数因子,只保留增长最快的项。

3n² + 5n + 2 ⇒ O(n²)

当 n = 1000 时,n² 项是 3 000 000,而 5n 只有 5000。较小的项几乎不改变总数。

这些类别反复出现,按增长从慢到快排列:

  • O(1),常数:通过下标读取数组元素。
  • O(log n),对数:二分查找,每一步使问题减半。
  • O(n),线性:对所有项目遍历一次,例如找最大值。
  • O(n log n):好的排序算法,例如归并排序。
  • O(n²),平方:对项目做两层嵌套循环,例如冒泡排序。
  • O(2ⁿ),指数:尝试每一个子集。它很快就变得不可用。
ntlog nnn log nn²2ⁿ

大 O 是一个上界:工作量增长最多这么快。Ω(大欧米伽)是下界,Θ(西塔)是紧确界,既是上界也是下界。

对数的底在大 O 中无关紧要,因为不同底的对数只相差一个常数因子。

自测 4n + 2n² + 100 的大 O 是什么?
  1. O(100)
  2. O(2n²)
  3. O(n)
  4. O(n²)

答案: O(n²). 对大的 n,n² 项占主导,常数因子 2 被舍去。O(n) 保留了错误的项,O(2n²) 忘了舍去常数。

3查找、排序和递归

几乎每门课程都会出现一些算法。把它们的复杂度当作事实记住。

  • 线性查找逐项检查:O(n)。二分查找需要已排序的数组,每一步使范围减半:O(log n)。
  • 冒泡、插入和选择排序在嵌套循环中比较并交换项目:O(n²)。对于已经排好序的数组,插入排序是 O(n)。
  • 归并排序把数组分成两半,分别排序再合并:任何情况下都是 O(n log n),额外内存为 O(n)。
  • 快速排序围绕一个枢轴划分:平均 O(n log n),但最坏情况是 O(n²),例如以第一个元素为枢轴去排已排好序的数组。
  • 只靠比较项目的排序,在最坏情况下不可能比 Ω(n log n) 更好。计数排序更快,因为它不做比较。

递归函数在更小的输入上调用自身,直到遇到可以直接回答的基本情形。它的运行时间写成递推式:

  • T(n) = T(n/2) + O(1) 给出 O(log n),像二分查找。
  • T(n) = 2T(n/2) + O(n) 给出 O(n log n),像归并排序。
  • T(n) = T(n − 1) + O(n) 给出 O(n²)。
  • T(n) = 2T(n − 1) + O(1) 给出 O(2ⁿ)。

有三种设计思想值得了解。分治是拆分、求解再合并。贪心每一步选取局部最优的选择。动态规划存储重叠子问题的答案,使它们只计算一次。

自测 快速排序的最坏情况时间复杂度是多少?
  1. O(log n)
  2. O(n log n)
  3. O(n)
  4. O(n²)

答案: O(n²). 枢轴不好时,每次划分都在一侧留下 n − 1 个项目,这给出 O(n²)。O(n log n) 是它的平均情况,也是归并排序的最坏情况。

4分步方法

要求一段代码的时间复杂度:

  1. 确定 n 是什么:输入的规模。
  2. 数出每个循环运行的次数。每次加 1 的循环大约运行 n 次。每次翻倍或减半的循环大约运行 log₂ n 次。
  3. 嵌套的循环相乘。依次执行的循环相加。
  4. 对于递归函数,写出递推式:调用多少次、规模多大,再加上每次调用的工作量。
  5. 保留增长最快的项,舍去常数。
  6. 用一个小的数检验,例如 n = 8 或 n = 1024。

对于理论题,先指出它所描述的算法或思想,再回忆它的复杂度及条件(已排序的输入、非负权重、重叠子问题)。

5例题讲解

跟着每一步走。打开下一步之前,先试着预测它。

例题讲解

这段代码的时间复杂度是多少?for i = 1 to n: (j = 1; while j < n: j = j × 2)

1

指明输入规模

n 是循环运行到的数。

2

数外层循环

i 从 1 到 n,步长为 1,所以外层循环运行 n 次。

3

数内层循环

j 依次取 1、2、4、8、…,在达到 n 时停止。经过 k 轮后 j = 2k,所以循环大约经过 log₂ n 轮后停止。

4

合并各循环

内层循环在外层循环里面,所以次数相乘。

n · log₂ n ⇒ O(n log n)
5

用一个数检验

对 n = 1024,内层循环运行 log₂ 1024 = 10 次,所以循环体运行 1024 · 10 = 10 240 次。这远少于 n² = 1 048 576。

答案

O(n log n)

6常见错误

  • 把依次运行的循环相乘。n + n = 2n 仍然是 O(n)。
  • 保留常数,如 O(2n) 或 O(n/2)。两者都是 O(n)。
  • 把翻倍的循环算作 n 轮。它大约运行 log₂ n 次。
  • 对未排序的数据使用二分查找,或者对无法一步到达中间的链表使用二分查找。
  • 题目问最坏情况时,却给出快速排序的平均情况。
  • 以为大 O 表示“精确”。O(n²) 是上界;Θ(n²) 才是精确的增长。
自测 一个函数先运行 n 次迭代的循环,然后另外再运行一个 n 次迭代的循环。它的时间复杂度是多少?
  1. O(2n)
  2. O(log n)
  3. O(n)
  4. O(n²)

答案: O(n). 依次执行的循环相加:n + n = 2n,舍去常数,得 O(n)。只有当一个循环在另一个里面时,O(n²) 才是对的。

7考试题是什么样的

  • 简单:关键术语和事实。二分查找、归并排序或数组访问的复杂度,O、Ω 和 Θ 的含义,什么是递归。
  • 中等:应用概念。一段代码的复杂度、要解的递推式、n 加倍时时间如何变化、哪个算法适合某项任务。
  • 困难:情形与分析。主定理、快速排序的最坏情况、贪心与动态规划的比较,以及 P、NP 和 NP 完全这几个类。

P 包含可以在多项式时间内解决的问题。NP 包含其解可以在多项式时间内验证的问题。P 中的每个问题都在 NP 中;P = NP 是否成立仍然未知。

在应用中一步一步地学在应用中一步一步地学

例题

例题 1简单

哪种记号表示增长的上界?

  1. ABig Omega
  2. BLittle omega
  3. CBig O
  4. DBig Theta
显示提示
  1. “至多”。
  2. 两个错误答案已被划掉。剩下的两个中有一个是正确的。
显示答案和解析

为什么是这个答案

O 表示上界,Ω 表示下界,Θ 表示紧确界(tight bound)。

答案

C · Big O
例题 2中等

在一个有序数组中,你从两端各放一个指针向内移动,寻找和为目标值的两个元素。时间复杂度是多少?

  1. AO(n)
  2. BO(n log n)
  3. CO(n²)
  4. DO(log n)
显示提示
  1. 每一步把其中一个指针移动一个位置。
  2. 两个错误答案已被划掉。剩下的两个中有一个是正确的。
显示答案和解析

为什么是这个答案

如果和太小,左指针右移;如果和太大,右指针左移,因此最多 n 步。

答案

A · O(n)
例题 3困难

Strassen 算法在矩阵乘法中对半尺寸的分块只做 7 次而不是 8 次递归乘法。它的运行时间是多少?

  1. AΘ(n² log n)
  2. BΘ(n2)
  3. CΘ(n³)
  4. DΘ(nlog₂ 7),约为 n2.81
显示提示
  1. 递推式为 T(n) = 7T(n/2) + O(n²)。
  2. 两个错误答案已被划掉。剩下的两个中有一个是正确的。
显示答案和解析

为什么是这个答案

取 a = 7、b = 2,nlog₂ 7 压过 O(n²) 的加法工作量,结果约为 n2.81。

答案

D · Θ(nlog₂ 7),约为 n2.81

问与答

什么是算法与复杂度?

两个算法可以解决同一个问题,但所用的时间可能相差很大。复杂度描述的是输入变大时工作量如何增长。

输入的规模称为 n:要排序的项目数、列表的长度、图中的节点数。

如何一步步学习算法与复杂度?
  1. 复杂度衡量什么
  2. 大 O 和增长类别
  3. 查找、排序和递归
  4. 分步方法
  5. 例题讲解
  6. 常见错误
  7. 考试题是什么样的
算法与复杂度练习包括哪些内容?
  • 简单: 关键术语与事实
  • 中等: 概念应用
  • 困难: 案例与分析

每道题有四个选项,只有一个正确。 卡住了?先打开一个小提示,需要时再查看完整解析。

练习方式

  • 每道题有四个选项,只有一个正确。
  • 卡住了?先打开一个小提示,需要时再查看完整解析。
  • 你的进度、错题和收藏的题目都会保留,方便考前复习。

本主题的前后主题

接下来学习

包含此主题的课程

计算机科学中的更多主题

可打印练习卷

免费练习算法与复杂度立即练习算法与复杂度每天 5 道免费题目,无需绑定银行卡。