算法学习(0)时间复杂度和空间复杂度
1、时间复杂度: to do的执行次数。
例如:一个100*100的二维数组,数组每一行都是1到100,计算数组中所有数字之和。
公式法时间复杂度为1
void addOneByOne(int arr[][100],int n){
int sum=0;
sum=n*n*(n+1)/2
printf("result is:%d",sum);
}
而如下方法时间复杂度为 10 0 2 100^2 1002
#include<iostream>
using namespace std;
void addOneByOne(int arr[][100],int n){ //不能写成int arr[][n]
int sum=0;
for(int i=0;i<n;i++){
for (int j=0;j<n;j++){
sum+=arr[i][j];
}
}
printf("result is:%d",sum);
}
int main(){
int n=100;
int ar[100][100]={0};//定义二维数组时,大小要写实数
int a=0;
for(int i=0;i<n;i++){
a=1;
for (int j=0;j<n;j++){
ar[i][j]=a;
a++;
}
}
addOneByOne(ar,100); //调用函数,变量是二维数组时只用写变量名
system("pause");
return 0;
}
2、常见的耗费时间的大小关系:
3、几种常见的阶
(1)常数阶
比如:
n=n+1;
n=n+1;
n=n+1;
n=n+1;
n=n+1;执行了4次,是常数级,用O(1)表示;即使是重复了100次,也是用O(1)表示。
再比如:
sum=n*(n+1)/2; 时间复杂度也为O(1)
(2)线性阶
执行次数随着问题规模线性变化。如单个for循环或单个while循环。
(3)平方阶
主要是双层循环嵌套。
(4)对数阶
在二分查找和二叉树中经常会有O(logn),其本质都可以简化成如下模型:
int count=1;
int n=64;
int items=0;
while(count<n){
count=count*2; //to do注意这行代码执行次数与n的关系
items++;
}
可以看出,当执行第七次时,count已经不满足count<n,执行次数x与n的关系正好是2^x=n,x=logn。
所以二分查找每循环一次都减半,所以to do的执行次数是logn。
4、举例:计算三层嵌套循环的时间复杂度
for(int i=1; i<=n; i++){
for(int j=1; j<=i; j++){
for(int k=1; k<=j; k++)
@ x += delta;
}
}
时间复杂度计算:
1+(1+2)+(1+2+3)+(1+2+3+4)+…+(1+2+3+4+…+n)
=
∑
i
=
1
n
i
(
i
+
1
)
2
=
1
12
n
(
n
+
1
)
(
2
n
+
4
)
=\sum\limits_{i=1}^n\frac{i(i+1)}{2}\\ =\frac{1}{12}n(n+1)(2n+4)
=i=1∑n2i(i+1)=121n(n+1)(2n+4)
数学小知识: ∑ i = 1 n i 2 = n ( n + 1 ) ( 2 n + 1 ) 6 \sum\limits_{i=1}^ni^2=\frac{n(n+1)(2n+1)}{6} i=1∑ni2=6n(n+1)(2n+1)
5、空间复杂度
申请一个或者是两三个变量,空间复杂度是O(1);
申请一个数组、链表、队列、栈或者Hash,空间复杂度是O(n);
申请一个二维数组,空间复杂度是O(
n
2
n^2
n2);