时间复杂度想必大家都有所了解,就是求法可能会有些迷茫,下面我来谈谈我的理解
概念:很多情况下可以通过考察一个程序中关键操作的执行次数,来估算算法的渐近时间复杂度
下面是矩阵乘法的例子
for(i=0;i<n;i++) //执行n+1次
for(j=0;j<n;j++){ //n(n+1)
c[i][j]=0; //n*n
for(k=0;k<n;k++) //n*n*(n+1)
c[i][j]+=a[i][k]*b[k][j]; //n*n*n
}
上例总的执行次数T(n)=2n3+3n2+2n+1
所以O(T(n))=n3
或者可以把c[i][j]+=a[i][k]*b[k][j];看作关键操作,可得O(n3)
这时可能会有很多向我一样觉得这如此简单,三个for循环直接相乘就完了直接O(n3),于是遇见for循环就相乘屡试不爽。
当看到邻接表就有问题了
遍历邻接表
struct ENode{
int adjVex;
ENode* nextArc;
};
for(i=0;i<n;i++) //n
for(ENode* w=a[i];w;w=w->nextArc)//a代表上图数组
if(!visit[i])//代表i是否访问过 //总共e次
{
visit[i]=1;
cout<<w->adjVex;
}
想着两个for相乘即O(n*e),但答案是O(n+e)
其实仔细想想也是,回到程序的关键操作,可以看出有两个关键操作,最外层for循环n次,内层和外层一起总共执行了e条边,所以是O(n+e)
还有就是为什么无向图遍历不是O(n+2e),而是O(n+e),我的理解是根据定义(设有非负函数f(n)和g(n),存在常数c>0和n0,使得当n>=n0时,有f(n)<=c*g(n),则记为f(n)=O(g(n)),称为大O记号),即可以让常数c变大使(n+e)*c依旧大于等于f(n)。所以大O记号里一般都没有常数。
再来一个例子,这个时间复杂度又是什么呢
先找出关键操作
//矩阵连乘主要代码
for(int i=0;i<n;i++)m[i][i]=0;
for(int r=2;r<=n;r++)
{
for(int i=0;i<=n-r;i++)
{
int j=i+r-1;
m[i][j]=m[i][i]+m[i+1][j]+p[i]*p[i+1]*p[j+1];
s[i][j]=i;
for(int k=i+1;k<j;k++)
{
int t=m[i][k]+m[k+1][j]+p[i]*p[k+1]*p[j+1];//当做关键操作
if(t<m[i][j]){
m[i][j]=t;
s[i][j]=k;
}
}
}
}
所认为的关键操作已经标记出来了,就计算它执行的次数即可。
r值 | i范围 | j 范围 | k的执行次数 |
---|---|---|---|
2 | [0,n-2] | [1,n-1] | [0,n-2] |
3 | [0,n-3] | [2,n-1] | [1,n-2] |
4 | [0,n-4] | [3,n-1] | [2,n-2] |
5 | [0,n-5] | [4,n-1] | [3,n-2] |
… | … | … | … |
n | [0,0] | [n-1,n-1] | [n-2,n-2] |
综上关键操作执行次数
T(n)=1*0+2*1+3*2+4*3+…+(n-1)*(n-2)=(n
3-n)/3
(即k中所有数相加,一个0次,两个1次,三个2次,…)
则O(T(n))=O(n3)