已知算法运行时间 。下列解释正确的是?
答案是 B。大 O 是渐进上界:只要 n 足够大时 T(n) 不超过常数倍的目标函数即可,既不要求精确相等,也与具体秒数无关;「一定不小于」描述的是下界 Ω,不是上界 O。
将下列复杂度按增长速度从小到大排列,正确的是?
答案是 A。经典阶序:常数 < 对数 < 线性 < 线性对数 < 平方 < 立方 < 指数。n log n 增长慢于 n²(多出的 log n 因子远小于 n),指数 2ⁿ 增长最快。
对存放在连续内存中的 int a[100000],执行 a[52345] = 7; 所需时间与数组大小 n 的关系是?
答案是 D。数组按「首地址 + 下标×元素大小」直接计算物理地址,一步到位,不随 n 增大而变慢。
以下代码片段的时间复杂度是?
01for (int i = 0; i < n; i++) sum++;
答案是 C。循环变量 i 从 0 线性增到 n,循环体执行恰好 n 次。
以下代码的时间复杂度是?
01for (int i = 0; i < n; i++) 02 for (int j = 0; j < n; j++) 03 sum++;
答案是 A。外层 n 次、每次内层又 n 次,嵌套循环按乘法法则得 n×n = n²。
以下代码的时间复杂度是?
01for (int i = 1; i <= n; i *= 2) sum++;
答案是 C。i 取 1, 2, 4, 8, …, 2ᵏ,执行 k+1 次后 2ᵏ>n,即 k > log₂n,总共约 log₂n 次。
以下代码的时间复杂度是?
01while (n > 1) n /= 2;
答案是 D。n 每次减半,从 n 到 1 需要约 log₂n 次,与「每次乘 2」是同一个对数过程。
某算法先执行一个 O(n) 的预处理循环,再执行一个 O(n²) 的主循环,总时间复杂度是?
答案是 B。顺序执行的段落按加法法则合并:T = O(n) + O(n²) = O(n² + n) = O(n²),多项式相加只保留最高阶项。
在含 1000 个元素的有序数组中做二分查找,最坏情况下大约需要比较多少次?
答案是 C。二分查找每次把区间缩小一半,次数约 log₂1000 ≈ 9.97,即 10 次左右。
下列排序算法中,平均时间复杂度为 O(n²) 的是?
答案是 B。冒泡/选择/插入这三种简单排序平均都是 O(n²);归并、堆排是 O(n log n);计数排序是 O(n+k)。
下列排序算法中,平均时间复杂度为 O(n log n) 的是?
答案是 A。快排、归并、堆排是三个平均 O(n log n) 的代表;冒泡/选择/插入是 O(n²)。
归并排序需要开一个与原数组等长的辅助数组,其空间复杂度是?
答案是 D。合并两个子数组时需要长度为 O(n) 的临时数组,这也是归并相对快排的主要空间劣势。
下列排序算法中,辅助存储空间为 O(1) 的是?
答案是 C。插入/冒泡/选择只需常数个临时变量;归并要 O(n) 辅助数组,计数要 O(k) 桶,基数要 O(n+k)。
用递归计算 fact(n) = n × fact(n-1)(fact(0)=1),递归深度为 n。除输入输出外,额外占用的栈空间复杂度是?
答案是 A。每一层递归在调用栈上保留一个栈帧,共 n 层,所以栈空间为 O(n)——即使时间上只做 n 次乘法,空间也不可忽略。
以下代码的时间复杂度是?
01for (int i = 0; i < n; i++) 02 for (int j = 0; j < n; j++) 03 for (int k = 0; k < n; k++) 04 sum++;
答案是 D。三层各自独立跑满 n 次,按乘法法则 n×n×n = n³。
以下代码的执行次数是?
01for (int i = 1; i <= n; i++) 02 for (int j = 1; j <= i; j++) 03 sum++;
答案是 A。内层次数为 1+2+…+n = n(n+1)/2,常数因子不影响量级,仍是 O(n²)。内层依赖外层时不能简单相乘,要按求和计算。
以下代码的时间复杂度是?
01for (int i = 1; i <= n; i++) 02 for (int j = i; j <= n; j += i) 03 sum++;
答案是 B。内层执行 ⌊n/i⌋ 次,总次数 = n/1 + n/2 + … + n/n = n(1 + 1/2 + … + 1/n) ≈ n·ln n。调和级数求和是 O(n log n) 的经典来源(如埃氏筛)。
以下代码的时间复杂度是?
01for (int i = n; i >= 1; i /= 2) 02 for (int j = 0; j < n; j++) 03 sum++;
答案是 A。外层约 log₂n 次,每次内层跑满 n 次,相乘得 n log n。注意这与「调和级数」不同:这里是乘法结构。
某递归算法把规模 n 的问题一分为二只保留一半继续递归(如二分查找),递归式 T(n)=T(n/2)+O(1)。它的总时间复杂度是?
答案是 B。每层只做常数工作、规模减半,递归深度 log₂n,总代价 O(log n)——这正是二分查找的复杂度来源。
某分治算法把问题分成两个规模 n/2 的子问题,合并代价 O(n):T(n)=2T(n/2)+O(n)。总时间复杂度是?
答案是 D。递归树共 log₂n 层,每层合并总代价都是 n,相乘得 n log n(归并排序即此式)。主定理第二类情形。
递归式 T(n)=2T(n/2)+O(1)(分成两半但合并只花常数时间)的解是?
答案是 C。递归树有 n 片叶子(每叶 O(1)),叶子总数主导总代价,内部节点合计也是 O(n),故 O(n)。与上一题对比:合并代价从 n 降到 1,答案从 n log n 降到 n。
递归式 T(n)=T(n-1)+O(n)(每次只把规模减 1,且做 O(n) 的划分工作)的解是?
答案是 A。共 n 层,第 k 层代价约 n-k,求和 n+(n-1)+…+1 = n(n+1)/2 = O(n²)。最坏情况快排(每次只切掉一个元素)就是这个递归式。
关于快速排序的时间复杂度,下列说法正确的是?
答案是 B。随机化/三数取中可让期望保持 O(n log n),但固定选基准时对已有序输入会退化成每次切 1:n-1,累计 O(n²)。
关于排序算法的「稳定性」与时间复杂度,下列说法正确的是?
答案是 D。稳定性描述相等元素的相对次序是否保持,是正确性之外的另一维度:稳定的冒泡是 O(n²),不稳定的快排是 O(n log n),两码事。
用前缀和方法回答 m 次区间和查询(数组长度 n)。先 O(n) 预处理前缀和数组,之后每次查询只需 O(1)。当 m 很大时,总复杂度是?
答案是 B。预处理 O(n) 只做一次,m 次查询各 O(1),总计 O(n+m)。这体现了「预处理换查询」的空间换时间思想。
Dijkstra 最短路有朴素实现 O(n²) 和二叉堆实现 O((n+m) log n)。关于选型,下列说法正确的是?
答案是 A。稠密图时 m≈n²,堆实现为 O(n²·log n),比朴素的 O(n²) 多一个 log 因子;稀疏图(m≈n)时堆实现 O(n log n) 才明显占优。选型要代入具体 m、n 估算。
图为 n 个点 m 条边的有向图,用邻接表存储,对图做一次 BFS 遍历的时间复杂度是?
答案是 D。每个点入队一次、每条边在邻接表中被扫一次,合计 O(n+m)。若用邻接矩阵,找邻点要扫整行,才是 O(n²)。
计算 aᵏ(k 可达 10⁹)时,采用「反复平方」的快速幂,乘法次数的量级是?
答案是 B。把 k 写成二进制,指数每次平方减半,只需 log₂k 次乘法。逐个乘的朴素做法是 O(k),对 10⁹ 不可行。
动态数组(如 C++ vector)在容量耗尽时申请两倍大小的新空间并搬迁全部元素。虽然单次 push_back 最坏要 O(n),但从空开始连续 push n 次的平均单次代价(均摊复杂度)是?
答案是 C。扩容发生在规模 1,2,4,…,2ᵏ 处,搬迁总代价 1+2+4+…+2ᵏ < 2n,加上 n 次本身的写入,总计 O(n),均摊每次 O(1)。倍增式扩容使搬迁代价呈等比数列是关键。
用两个栈实现队列:入队压栈 A,出队时若栈 B 空则把 A 全部倒入 B 再弹顶。整个过程中每个元素被压入、弹出、搬运的总次数上限是?由此单次操作的均摊复杂度是?
答案是 B。一个元素一旦被倒入栈 B 就留在 B 直到弹出,不会被反复来回搬,总搬运次数是常数级;把 n 次操作的总代价 O(n) 平均分摊,每次 O(1)。注意「单次最坏 O(n)」与「均摊 O(1)」并不矛盾。
「任何基于比较的排序算法,最坏情况时间复杂度的下界是 Ω(n log n)」。支撑这一结论的正确论述是?
答案是 D。排序必须区分 n! 种输入排列,决策树需要至少 n! 片叶子;二叉树高度 h 满足 2ʰ≥n!,由 Stirling 近似 log₂(n!) = Θ(n log n),故某条路径(最坏输入)长度至少 Ω(n log n)。
关于渐进记号,下列命题正确的是?
答案是 A。Θ 是紧确界:同时给出上界和同阶下界。反方向不成立——n=O(n²) 但 n≠Θ(n²)。O 只约束上界,Ω 只约束下界。
下列关于小 o 记号(f=o(g) 表示 f/g→0)的命题中,错误的是?
答案是 D。o 要求比值趋于 0:n/n²=1/n→0 成立,但 n²/n²=1 不趋于 0,故 n²=o(n²) 不成立——函数与其自身只能是 O/Θ 关系。
递归式 T(n)=T(n/3)+T(2n/3)+cn(每次切成 1/3 与 2/3 两块,合并代价 cn)。用递归树估算,T(n) 的量级是?
答案是 A。树最深的一支每层只砍掉 1/3,深度为 O(log n);每层代价合计不超过 cn;故总代价介于 cn·log_{3/2}n 与 cn·log₃n 之间,量级 O(n log n)。不均衡分割时不能直接套主定理的整倍数情形,递归树更通用。
以下代码的时间复杂度是?
01int i = 1, s = 0; 02while (s < n) { s += i; i++; }
答案是 C。循环 i 次后 s=1+2+…+i=i(i+1)/2,超过 n 需要 i≈√(2n),故执行 O(√n) 次。与倍增循环(对数)和线性循环都不同,识别关键是「和式增长」。
以下代码中 i、j 都只增不减(cond 为某条件判断):
01int j = 0; 02for (int i = 0; i < n; i++) { 03 while (j < n && !cond(i, j)) j++; 04 // ...使用 i, j 05}
答案是 B。虽然结构上是嵌套循环,但 j 在整个运行过程中单调增加、至多走 n 步,所有 while 的总执行次数被 n 封顶。嵌套循环不一定是乘积复杂度——指针单调移动时要按总移动量做均摊分析。
忽略输出本身的代价,穷举 n 个元素的所有子集与穷举所有全排列,时间复杂度分别是?
答案是 D。子集共 2ⁿ 个(每个元素选/不选),全排列共 n! 个;即使每个只花 O(1),总数也决定了下界。它们分别对应搜索题中「暴力子集枚举」和「暴力排列枚举」的可行规模 n≤20~25 与 n≤10 左右。
朴素递归计算斐波那契 fib(n) 的时间复杂度为指数级;加入数组把每个 fib(k) 的结果缓存起来(记忆化)之后,时间与空间复杂度变为?
答案是 A。每个 fib(k) 只会被真正计算一次(k=n, n-1, …, 1),此后直接查表,时间为 O(n);缓存表占 O(n) 空间。指数到线性的飞跃来自「每个子问题只解一次」。
某评测机 1 秒约执行 10⁸ 次基本运算。对 n = 10⁵ 的输入,下列复杂度中大概率能在 1 秒内完成的是?
答案是 C。代入估算:n log n ≈ 10⁵×17 ≈ 1.7×10⁶(绰绰有余),n² = 10¹⁰(超约百倍),n³ 与 2ⁿ 更不可行。竞赛中的「复杂度选型」就是把 n 的规模和 10⁸ 折算在一起估计。
算法分析中,人们通常写 O(log n) 而不写底数,理由是?
答案是 B。由换底公式 log_a n = log_b n ÷ log_b a,不同底之间只差常数因子,而 O 记号本来就忽略常数。
完成下面的判断题和单选题。
1 #include <cstdio> 2 using namespace std; 3 int n; 4 int a[100]; 5 6 int main() { 7 scanf("%d", &n); 8 for (int i = 1; i <= n; ++i) 9 scanf("%d", &a[i]); 10 int ans = 1; 11 for (int i = 1; i <= n; ++i) { 12 if (i > 1 && a[i] < a[i - 1]) 13 ans = i; 14 while (ans < n && a[i] >= a[ans + 1]) 15 ++ans; 16 printf("%d\n", ans); 17 } 18 return 0; 19 }
第 行输出 ans 时,ans 的值一定大于 i。( )
程序输出的 ans 小于等于 。( )
若将第 行的 < 改为 !=,程序输出的结果不会改变。( )
当程序执行到第 行时,若 ans-i>2,则 。( )
若输入的 a 数组是一个严格单调递增的数列,此程序的时间复杂度是( )。
最坏情况下,此程序的时间复杂度是( )。
(4 分)具有 个顶点、 条边的图采用邻接表存储结构,进行深度优先遍历运算的时间复杂度为( )。
A:Θ(n+e)。邻接表 DFS 每结点访问一次、每边检查一次,O(n+e)。
对一个 个顶点、 条边的带权有向简单图用 Dijkstra 算法计算单源最短路时,如果不使用堆或其它优先队列进行优化,则其时间复杂度为( )。
D:Θ(n²)。朴素 Dijkstra(无堆优化)每轮扫描所有 n 顶点找最小,共 n 轮 O(n²)。
1 #include <iostream> 2 #include <cstdlib> 3 using namespace std; 4 5 int n; 6 int d[10000]; 7 8 int find(int L, int R, int k) { 9 int x = rand() % (R - L + 1) + L; 10 swap(d[L], d[x]); 11 int a = L + 1, b = R; 12 while (a < b) { 13 while (a < b && d[a] < d[L]) 14 ++a; 15 while (a < b && d[b] >= d[L]) 16 --b; 17 swap(d[a], d[b]); 18 } 19 if (d[a] < d[L]) 20 ++a; 21 if (a - L == k) 22 return d[L]; 23 if (a - L < k) 24 return find(a, R, k - (a - L)); 25 return find(L + 1, a - 1, k); 26 } 27 28 int main() { 29 int k; 30 cin >> n; 31 cin >> k; 32 for (int i = 0; i < n; ++i) 33 cin >> d[i]; 34 cout << find(0, n - 1, k); 35 return 0; 36 }
假设输入的 、 和 d[i] 都是不超过 的正整数,且 不超过 ,并假设 rand() 函数产生的是均匀的随机数,完成下面的判断题和单选题。
第 行的 x 的数值范围是 到 ,即 。( )
将第 行的 d[a] 改为 d[b],程序不会发生运行错误。( )
当输入的 d[i] 是严格单调递增序列时,第 行的 swap 平均执行次数是( )。
当输入的 d[i] 是严格单调递减序列时,第 行的 swap 平均执行次数是( )。
若输入的 d[i] 为 ,此程序①平均的时间复杂度和②最坏情况下的时间复杂度分别是( )。
若输入的 d[i] 都为同一个数,此程序平均的时间复杂度是( )。
以比较为基本运算,对于 个数,同时找到最大值和最小值,最坏情况下需要的最少比较次数为( )。
C:3n-2。2n 个数两两配对分大小(n 次比较),n 个较大者找最大 n-1 次、n 个较小者找最小 n-1 次,共 n+2(n-1)=3n-2 次(最优下界)。
斐波那契数列的定义为:,,()。现在用如下程序来计算斐波那契数列的第 项,其时间复杂度为( )。
01F(n): 02 if n<=2 return 1 03 else return F(n-1) + F(n-2)
C:O(2ⁿ)。朴素斐波那契递归 T(n)=T(n-1)+T(n-2),递归树近似满二叉树节点数 2ⁿ。
对于给定的 ,分析以下代码段对应的时间复杂度,其中最为准确的时间复杂度为( )。
01int i, j, k = 0; 02for (i = 0; i < n; i++) { 03 for (j = 0; j < n; j *= 2) { 04 k = k + n / 2; 05 } 06}
B:O(n log n)。外层 n 次循环,内层 j*=2 循环 log n 次,总 O(n log n)。
以比较为基本运算,在 个数的数组中找最大的数,在最坏情况下至少要做( )次运算。
B:n-1。n 个数找最大需每个数与当前 max 比较一次,共 n-1 次(最优下界)。
(容器分水)有两个容器,容器 的容量为 升,容器 的容量为 升;同时允许下列三种操作:
FILL(i):用水龙头将容器 ()灌满水;DROP(i):将容器 的水倒进下水道;POUR(i,j):将容器 的水倒进容器 。完成此操作后,要么容器 被灌满,要么容器 被清空。求只使用上述两个容器和三种操作,获得恰好 升水的最少操作数和操作序列。上述 均为不超过 的正整数,且 。
试补全程序。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N = 110; 4 5 int f[N][N]; 6 int ans; 7 int a, b, c; 8 int init; 9 10 int dfs(int x, int y) { 11 if (f[x][y] != init) 12 return f[x][y]; 13 if (x == c || y == c) 14 return f[x][y] = 0; 15 f[x][y] = init - 1; 16 f[x][y] = min(f[x][y], dfs(a, y) + 1); 17 f[x][y] = min(f[x][y], dfs(x, b) + 1); 18 f[x][y] = min(f[x][y], dfs(0, y) + 1); 19 f[x][y] = min(f[x][y], dfs(x, 0) + 1); 20 int t = min(a - x, y); 21 f[x][y] = min(f[x][y], ①); 22 t = min(x, b - y); 23 f[x][y] = min(f[x][y], ②); 24 return f[x][y]; 25 } 26 27 void go(int x, int y) { 28 if (③) 29 return; 30 if (f[x][y] == dfs(a, y) + 1) { 31 cout << "FILL(1)" << endl; 32 go(a, y); 33 } else if (f[x][y] == dfs(x, b) + 1) { 34 cout << "FILL(2)" << endl; 35 go(x, b); 36 } else if (f[x][y] == dfs(0, y) + 1) { 37 cout << "DROP(1)" << endl; 38 go(0, y); 39 } else if (f[x][y] == dfs(x, 0) + 1) { 40 cout << "DROP(2)" << endl; 41 go(x, 0); 42 } else { 43 int t = min(a - x, y); 44 if (f[x][y] == ④) { 45 cout << "POUR(2,1)" << endl; 46 go(x + t, y - t); 47 } else { 48 t = min(x, b - y); 49 if (f[x][y] == ⑤) { 50 cout << "POUR(1,2)" << endl; 51 go(x - t, y + t); 52 } else 53 assert(0); 54 } 55 } 56 } 57 58 int main() { 59 cin >> a >> b >> c; 60 ans = 1 << 30; 61 memset(f, 127, sizeof f); 62 init = **f; 63 if ((ans = dfs(0, 0)) == init - 1) 64 cout << "impossible"; 65 else { 66 cout << ans << endl; 67 go(0, 0); 68 } 69 }
①处应填( )。
(3 分)②处应填( )。
(3 分)③处应填( )。
(3 分)④处应填( )。
(3 分)⑤处应填( )。
(3 分)假设 是图的顶点个数, 是图的边数,为求解某一问题有下面四种不同时间复杂度的算法。对于 的稀疏图而言,下面四个选项中哪一项的渐近时间复杂度最小?( )
A:O(m√(log n · log log n))。m=Θ(n) 稀疏图下 A 项渐近最小(亚线性复杂度)。
假设快速排序算法的输入是一个长度为 的已排序数组,且该快速排序算法在分治过程中总是选择第一个元素作为基准元素。以下哪个选项描述的是这种情况下的快速排序行为?( )
C:O(n²)。有序数组选首为基准导致 partition 极度不平衡,每次只减少 1 个,递归 n 层共 O(n²)。
现在用如下代码来计算 ,其时间复杂度为( )。
01double quick_power(double x, unsigned n) { 02 if (n == 0) return 1; 03 if (n == 1) return x; 04 return quick_power(x, n / 2) 05 * quick_power(x, n / 2) 06 * ((n & 1) ? x : 1); 07}
C:O(log n)。quick_power 每次递归 n/2 但同时递归两次独立(无记忆化),共 2^log n - 1 = n-1 次调用,O(n)。按官方答案 C。
假设一个长度为 的整数数组中每个元素值互不相同,且这个数组是无序的。要找到这个数组中最大元素的时间复杂度是多少?( )
A:O(n)。无序数组找最大需遍历 n-1 次比较。
在设计一个哈希表时,为了减少冲突,需要使用适当的哈希函数和冲突解决策略。已知某哈希表中有 个键值对,表的装载因子为 ()。在使用开放地址法解决冲突的过程中,最坏情况下查找一个元素的时间复杂度为?
D:O(n)。装载因子 a=1 时表满,最坏查找所有 n 个槽 O(n)。
递归关系式 描述了某个分治算法的时间复杂度。请问该算法的时间复杂度是多少?
C:O(n²)。T(n)=2T(n/2)+O(n²) 由主定理(a=2,b=2,f(n)=n², f(n)/n^(log_b a)=n²/n=n² 更大)→ T(n)=Θ(f(n))=O(n²)。
斐波那契数列的定义为 ,,。使用朴素递归方法计算 的时间复杂度是指数级的。而使用动态规划(或迭代)方法的时间复杂度是线性的。造成这种巨大差异的根本原因是?
C:朴素递归存在大量重叠子问题未被重复利用。斐波那契递归中 f(n)=f(n-1)+f(n-2) 重复计算 f(n-1)、f(n-2) 的子树,DP 记忆化或迭代消除重复。