研究时间复杂度的重要性
我们知道CPU提升的速度是很慢的,就算夸张点10年间提升了10000倍
如果一个可以有时间复杂度为
的算法的程序,我们写成了
,那么程序只提升了
倍 ,而对于
时间复杂度的算法却能提升10000倍
最坏情况和平均情况
示例:
在一个由n个元素的数组中,按顺序查找一个数
最好的情况是查找的就是第一个数,复杂度
平均运行时间需要从概率来看,也就是期望
每个数是查找的概率是
,然后乘以对应的随机变量
期望
所以平均查找次数是
次
最坏情况是这个数字在最后一个位置,所以需要查找n次
平均运行时间是最有意义的,因为这是一个通常的运行时间,比如除了双十一外的364天,淘宝运行时间都是1秒,但是最坏情况是双十一那天,淘宝运行时间可能是100秒
最坏情况运行时间是一种保证,也就是说运行时间不会再多了,这在实际应用中是一个很重要的需求,所以一般我们说的时间复杂度都是指最坏情况的运行时间
时间复杂度的渐进表示法
我们把语句的总的执行次数记作
是关于问题规模n的函数
大O表示法
- 表示存在常数 ,使得当 时有
对于充分大的
而言,
是
的某种上界
但是一个东西的上界可以有很多个,太大的上界对我们分析算法复杂度没有什么参考意义,我们希望跟真实情况越贴近越好
推导大O阶:
- 用常数取代运行时间中的所有加数常数
- 在修改后的运行次数函数中,只保留最高阶项
- 如果最高阶项且不是1,则去除与这个项相乘的常数 得到的结果就是大O阶
例1:
+
+
+
推导大O阶得:
例2:
例3:
- 表示存在常数 ,使得当 时有
- 表示同时有 和
常见的时间复杂度
常见的时间复杂度所耗时间的大小排名:
学数学的时候用的记忆技巧:对幂指阶超
空间复杂度分析
算法空间复杂度的计算公式记作: ,其中 为问题的规模, 为语句关于 所占存储空间的函数
一个程序执行时,除了需要存储程序本身的指令、常数、遍历和输入数据外,还要存储对数据操作的存储单元
如果输入数据所占空间只取决于问题本身,与算法无关,那我们只需要分析该算法在实现时所需的辅助空间,也就是分析额外使用的空间即可
例1:
下面的代码输入数据matrix所占空间与算法优劣没有关系,所以我们只需要看额外的使用空间即可
这里我们定义了变量:
,数组:
用大O推导法知
,其中
表示矩阵中的所有元素个数
(需要
数组来存储信息)
class Solution {
public:
vector<int> findDiagonalOrder(vector<vector<int>>& matrix) {
if(!matrix.size() || !matrix[0].size()) return {};
int n = matrix.size(), m = matrix[0].size();
vector <int> res;
int dx[] = {0, 1, 1, -1}, dy[] = {1, -1, 0, 1};
int x = 0, y = 0, d = 0;
for (int i = 1; i <= n * m; i ++) {
res.push_back(matrix[x][y]);
matrix[x][y] = INT_MAX;
if (i == n * m) break;
// 利用(a,b)找到下一个可以走的点
int a = x + dx[d], b = y + dy[d];
while(a < 0 || a >= n || b < 0 || b >= m || matrix[a][b] == INT_MAX) {
d = (d + 1) % 4;
a = x + dx[d], b = y + dy[d];
}
// 让(x,y)变成下一个可走的点,然后进入下一个循环
x = x + dx[d];
y = y + dy[d];
if (d == 0 || d == 2) d = (d + 1) % 4;
}
return res;
}
};
这里我额外用到的空间是常数阶的,所以空间复杂度是
虽然这里用到了rotateArray[i]等数组元素,但这是题目给的输入数据,不是自己额外定义的,所以不用算这一部分
class Solution {
public:
// 二分:二段性质 可能有重复
// 3 4 5 1 2 3
int minNumberInRotateArray(vector<int> rotateArray) {
int n = rotateArray.size();
if (n == 0) return 0;
// 去重
int i = 0, j = n - 1;
while(j >= 0 && rotateArray[j] == rotateArray[i]) j --;
// 递增的情况,返回第一个值
if (j < 0 || rotateArray[0] <= rotateArray[j]) return rotateArray[0];
// 二分
int l = i, r = j;
while(l < r) {
int mid = l + r >> 1;
if (rotateArray[mid] <= rotateArray[j]) r = mid;
else l = mid + 1;
}
return rotateArray[l];
}
};