本节课没有课上编程练习,重点学习算法的基本概念和评价方式。
算法是使用程序解决问题的方法。它是一组有限的、明确的操作步骤,用于在有限时间内解决特定问题或完成特定任务。
数据结构是数据的组织及操作方式。算法和数据结构是程序设计的两大核心,它们相辅相成——好的数据结构可以让算法更高效,好的算法也需要合适的数据结构来支撑。
算法可以通过以下三种方式进行描述:
问题:如何判断一个数n是奇数还是偶数?
自然语言描述:
流程图描述:
伪代码描述:
IF n % 2 == 0 THEN
输出偶数
ELSE
输出奇数评价一个算法好坏的标准主要包括以下几个方面:
时间复杂度描述算法执行所需要的步骤数或操作数,是衡量算法效率的重要指标。
时间复杂度的表示方法:
常见算法时间复杂度:
| 复杂度 | 运算次数 | 时间复杂度描述 | 数据规模 |
|---|---|---|---|
| O(1) | 常数次 | 常数时间复杂度 | 无限制 |
| O(logn) | logn | 对数时间复杂度 | n<2^100000000 |
| O(n) | n次 | 线性时间复杂度 | n<100000000 |
| O(nlogn) | nlogn | 线性对数时间复杂度 | n<5000000 |
| O(n²) | n² | 平方时间复杂度 | n<10000 |
| O(n³) | n³ | 立方时间复杂度 | n<500 |
| O(nᵏ) | nᵏ | k次方时间复杂度 | n<100 |
| O(2ⁿ) | 2ⁿ | 指数时间复杂度 | n<26 |
空间复杂度描述算法执行过程中临时占用存储空间的大小,同样是衡量算法效率的重要指标。
空间复杂度的表示方法:
在竞赛中,通常更关注时间复杂度,但也需要注意空间复杂度不能超过题目给定的内存限制。
下面通过一个具体例子,演示如何一步步分析时间复杂度。
问题:在一个长度为n的数组中,找出所有元素对(i, j)使得 arr[i] + arr[j] == target。
1for (int i = 0; i < n; i++) { // 外层循环执行 n 次
2 for (int j = i + 1; j < n; j++) { // 内层循环执行约 n/2 次(平均)
3 if (arr[i] + arr[j] == target) {
4 cout << i << " " << j << endl;
5 }
6 }
7}逐步分析:
对照数据规模表: O(n²) 对应的数据规模为 n<10000,所以当 n 超过 10000 时,这个算法可能会超时,需要考虑更优的解法。
拿到一道题后,如何判断该用什么复杂度的算法?
步骤1:看数据规模。 题目一般会给出 n 的范围,根据下表反推可以接受的时间复杂度:
步骤2:选择算法。 确定了可以接受的复杂度后,选择合适的算法来实现。
步骤3:验证。 粗略估算代码的运算次数,确保不会超过 10^8(一般情况下,1秒内可执行约 10^8 次简单运算)。
举例: 题目给出 n ≤ 1000,时限 1 秒。
ios::syncwithstdio(false) 和 cin.tie(0) 来加速。