目录
一. 时间复杂度概念
这样用大写O(来体现算法时间复杂度的记法,我们称之为大О记法。一般情况下,随着n的增大,T(n)增长最慢的算法为最优算法。
显然,由此算法时间复杂度的定义可知,我们的三个求和算法的时间复杂度分别为o(n),o(1),0(n2)。我们分别给它们取了非官方的名称,O(1)叫常数阶、O(n)叫线性阶、O(n2)叫平方阶,当然,还有其他的一些阶.
时间复杂度:0(1)序列被执行的次数,次数一定跟问题规模相关
例题1:
我们先看执行了多少条语句,分析如下:
当找到2时一共执行了六条语句 复杂度为O(1).
二. 推导大O阶
三. 几种常见的时间复杂度:
3.1常数阶:
首先顺序结构的时间复杂度。下面这个算法,也就是刚才的第二种算法(高斯算法),为什么时间复杂度不是O(3),而是O(1)。
这个算法的运行次数函数是f (n) =3。根据我们推导大О阶的方法,第一步就是把常数项3改为1。在保留最高阶项时发现,它根本没有最高阶项,所以这个算法的复杂度为O(1).
事实上无论n为多少,上面的两段代码就是3次和12次执行的差异。这种与问题的大小无关(n的多少),执行时间恒定的算法,我们称之为具有0(1)的时间复杂度,又叫常数阶。
注意:不管这个常数是多少,我们都记作0(1),而不能是o(3)、0(12)等其他任何数字.
3.2线性阶:
线性阶的循环结构会复杂很多。要确定某个算法的阶次,我们常常需要确定某个特定语句或某个语句集运行的次数。因此,我们要分析算法的复杂度,关键就是要分析循环结构的运行情况。
下面这段代码,它的循环的时间复杂度为0[n),因为循环体中的代码须要执行n次。
例题2:
首先一定是与n有关系的,10个数据循环五次,20个数据循环十次
所以时间复杂度为 O(n/2),但是由于推导大n阶 我们只保留最高阶项所以依旧为O(n)
3.3对数阶
由于每次count乘以2之后,就距离n更近了一分。也就是说,有多少个2相乘后大于 n,则会退出循环。由2x=n得到x=bgan。所以这个循环的时间复杂度为0(logn)。
3.4 平方阶:
例题3:
解题思路:
变式1:
解题思路
轮转了一个等差数列:首项 n-1 末项 1 等差数列的和 n的平方
时间复杂度O(n^2)
3.5 总结
常用的时间复杂度耗费时间排序:
O(n^2)>O(n)>O(logn)>O(1)
四、空间复杂度
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反映的是一个趋势, 空间复杂度比较常用的有:O(1)、O(n)、O(n^2)
4.1 空间复杂度O(1)
如果算法执行所需要的临时空间不随着某个变量n的大小而变化,即此算法空间复杂度为一个常量,可表示为O(1)
int i=1;
int j=2;
i++;
j++;
int m=i+j;
代码中的i、j、m所分配的空间都不随着处理数据量变化,因此它的空间复杂度为O(1)
4.2空间复杂度O(n)
int [] m=new int[n]
for(i=1;i<=n;i++)
{
i=j;
j++;
}
新new了一个数组出来,这个数组占用大小为n,因此,这段代码的空间复杂度为O(n)
例题4:数字排序 奇数在前偶数在后
void Ajust(int* arr, int len) {
int i = 0, j = len - 1;
while (i < j) {
//1. 从左-> 右元素查找,找到偶数值的位置停止
//奇数 继续向下查找
while (i<j && (arr[i] & 0x1) != 0) { //优化 arr[i]%2 != 0
i++;
}
//i 标记就是偶数值下标
//2. 从右 -> 左 元素查找 找奇数值所在位置停止
while (i<j && (arr[j] & 0x1) == 0) {
j--;
}
//j 标记奇数值的位置
if (i < j) {
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
}
int main() {
int arr[] = { 1,2,3,4,5,6,7,9 }; //1,3,5
int len = sizeof(arr) / sizeof(arr[0]);
Ajust(arr, len);
for (int i = 0; i < len; i++) {
printf("%-5d", arr[i]);
}
return 0;
}
注意:
运算符优先级问题
i<j && (arr[i] & 0x1) != 0 需要留意 按位与 和 逻辑与 还有 等号 的优先级问题
附上优先级表:
解决方法:
所以在运算时要加上小括号 保证先进行按位与运算 而不是 == 运算