华为可信科目一完整试学:复杂度、边界测试与错误定位
把复杂度判断变成可以照着做的检查:先估算规模,再选择结构;代码出错时,保留一组能稳定触发错误且尽量简单的输入,并记录修改后怎样重新测试。
开始前建议
- 理解循环与递归
- 做过至少一道数组或字符串题
- 示例语言
- C11 · C++17 · Java 17 · Python 3.11
- 验证方式
- 同一组输入输出逐语言运行
- 示例状态
- 2026-07-20 · 四语言均已验证
今天解决什么
把复杂度判断变成可以照着做的检查:先估算规模,再选择结构;代码出错时,保留一组能稳定触发错误且尽量简单的输入,并记录修改后怎样重新测试。
需要留下什么
可运行示例、一个最简单且仍会触发错误的输入,以及至少一道相关练习的提交记录。
做到什么算完成
能写出算法的最坏时间与额外空间复杂度
文末还有完整完成检查。
从数据规模反推操作预算
不要先背结论。把输入规模 n、每一步做多少工作、最坏情况下执行多少步写成式子,再判断是否可接受。嵌套循环不一定是平方复杂度;双指针若每个指针只单向移动,总移动次数仍可能是线性。
除时间外还要写空间:容器最多存多少元素、递归最深多少层、每个状态包含哪些字段。
- ✓单次扫描通常是 O(n)
- ✓排序通常是 O(n log n)
- ✓枚举所有二元组通常是 O(n²)
- ✓状态空间乘转移数后再判断图搜索成本
边界矩阵先于随机测试
针对输入结构建立矩阵:长度、数值、顺序、重复、字符类别和连通性。每一维至少选一个正常值和一个边界值,再挑可能相互影响的组合。
随机测试适合发现意外组合,但不能替代对需求边界的覆盖。先用矩阵保证已知风险,再用随机或对拍扩展。
- ✓长度:0、1、最大值
- ✓数值:最小、最大、负数、零
- ✓顺序:已排序、逆序、全部相等
- ✓结构:不连通、成环、多个入口或出口
把会出错的输入简化到仍能复现
出错后不要只修当前样例。删除无关元素、缩短字符串、减小数值,直到再删一步就不再出错。这样得到的简单输入更容易暴露真正原因,也方便重新检查代码。
修改后把这组简单输入加入重新测试清单,同时加入一个相邻但应成功的输入,避免改好一个问题却影响其他情况。
- ✓记录预期和实际输出
- ✓记录第一次错误发生的位置
- ✓只修改一个假设再重跑
- ✓保留修复前后的提交或代码片段
圈复杂度用于安排测试,不当分数
分支、循环和布尔短路会增加独立路径。用圈复杂度识别需要拆分或重点测试的函数,但不要机械追求一个固定数字;关键是每个判定是否有可命名的输入和可验证结果。
- ✓复杂分支先画控制流
- ✓重复条件提取为有名字的判断
- ✓大函数按职责拆分
- ✓对每条高风险路径准备回归输入
可运行最小示例
在有序数组中寻找第一个不小于目标值的位置。选择报名时确认的主语言,先运行最小示例,再修改输入观察语法、边界与状态更新怎样影响结果。
C++17标准库 vector,手写左闭右开二分
C++17 · 可运行示例
#include <iostream>
#include <vector>
using namespace std;
int main() {
int n;
if (!(cin >> n)) return 0;
vector<int> values(n);
for (int& value : values) cin >> value;
int target;
cin >> target;
int left = 0;
int right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (values[mid] >= target) right = mid;
else left = mid + 1;
}
if (left == n) cout << -1 << '\n';
else cout << left << ' ' << values[left] << '\n';
return 0;
}编译与运行命令
clang++ -std=c++17 -O2 -Wall -Wextra main.cpp -o main && ./main用来测试的输入
4
1 4 4 7
4预期输出
1 4C11GCC 11+ · 原生数组与左闭右开二分
C11 · 可运行示例
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int n;
if (scanf("%d", &n) != 1) return 0;
int *values = malloc((size_t)n * sizeof(*values));
if (n > 0 && values == NULL) return 1;
for (int i = 0; i < n; ++i) scanf("%d", &values[i]);
int target;
scanf("%d", &target);
int left = 0, right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (values[mid] >= target) right = mid;
else left = mid + 1;
}
if (left == n) puts("-1");
else printf("%d %d\n", left, values[left]);
free(values);
return 0;
}编译与运行命令
gcc -std=c11 -O2 main.c -o main && ./main用来测试的输入
4
1 4 4 7
4预期输出
1 4Java 17OpenJDK 17 · 原生数组与左闭右开二分
Java 17 · 可运行示例
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws Exception {
Scanner scanner = new Scanner(new BufferedInputStream(System.in));
if (!scanner.hasNextInt()) return;
int n = scanner.nextInt();
int[] values = new int[n];
for (int i = 0; i < n; i++) values[i] = scanner.nextInt();
int target = scanner.nextInt();
int left = 0;
int right = n;
while (left < right) {
int mid = left + (right - left) / 2;
if (values[mid] >= target) right = mid;
else left = mid + 1;
}
if (left == n) System.out.println(-1);
else System.out.println(left + " " + values[left]);
}
}编译与运行命令
javac Main.java && java Main用来测试的输入
4
1 4 4 7
4预期输出
1 4Python 3.11Python 3.11 · list 与左闭右开二分
Python 3.11 · 可运行示例
import sys
def main() -> None:
tokens = list(map(int, sys.stdin.buffer.read().split()))
if not tokens:
return
n = tokens[0]
values = tokens[1:1 + n]
target = tokens[1 + n]
left = 0
right = n
while left < right:
mid = left + (right - left) // 2
if values[mid] >= target:
right = mid
else:
left = mid + 1
if left == n:
print(-1)
else:
print(left, values[left])
if __name__ == "__main__":
main()编译与运行命令
python3 main.py用来测试的输入
4
1 4 4 7
4预期输出
1 4典型错误与最简单的出错例子
闭区间写法把 right 初始化为 n
若循环使用 left <= right,right 必须是最后一个合法下标 n-1。把 n 当成合法下标会在目标大于所有元素时越界。
错误片段 · C++17
int left = 0, right = n;
while (left <= right) {
int mid = left + (right - left) / 2;
if (values[mid] >= target) right = mid - 1;
else left = mid + 1;
}最简单的出错例子
3
1 4 7
8正确结果
-1错误表现
循环会访问 values[3],越过数组末尾怎么改:统一使用左闭右开区间 [left, right),初始化 right=n、循环条件 left<right;或完整改成闭区间并把 right 初始化为 n-1。
用站内 K 题完成应用练习
确认已经掌握
- □能写出算法的最坏时间与额外空间复杂度
- □二分区间含义、初始化和循环条件保持一致
- □空数组、单元素、目标小于最小值和大于最大值均已测试
- □一个会出错的输入已简化,并加入以后每次都要重新测试的输入集合
- □至少完成 2 道关联 K 题并保存复杂度说明