目录
一、时间复杂度的基本计算
算法的真实时间复杂度过程比较繁琐,公式如下图所示,其中 T 表示时间复杂性;k 表示元运算的种类;t 表示元运算需要的时间;e 表示某类元运算运行的次数;N 表示问题的规模;I 表示算法的输入。
同时还需要考虑三种情况下的时间复杂度,即最坏情况下、最好情况下和平均情况下的时间复杂性。其中 Dn 表示规模为N的合法输入的集合。P(I) 表示出现 I 的概率。
为了简化复杂性分析,通常我们只求他的最好情况下的复杂性。比如 时,我们可以简化复杂性为 。进一步考虑到分析算法的复杂性在于比较两个算法的运行效率,所以我们只需要关心阶数就可以了,确定算法的阶就可以判断那个算法的效率高。当然如上的考虑都是在问题的规模足够大的情况下,因为当时且单调增时,是T(N) 的渐近性态。
二、Ο 与 Ω 的定义与使用
接下来介绍渐进性态下的常用的两个符号:、。其中 用来评估算法的复杂性,得到的是输入规模充分大时时间复杂度的一个上界。这个上界越低越精准。 得到的则是下界,越高越精准。在平时使用过程中,出现频率最高的是 ,编写算法的时候考虑最坏情况下的时间复杂度。
举个例子:拿插入排序来说,插入排序的时间复杂度我们都说是 , 但是在数据本来有序的情况下时间复杂度是 ,也就对于所有输入情况来说,最坏是 的时间复杂度,所以称插入排序的时间复杂度为 。
但在看了关于 的定义之后,我感觉到一些矛盾: 是最坏情况下的时间复杂度,但为什么我们平时默认的一些算法复杂度并不是最坏情况下的复杂度呢,比如快速排序的复杂度,我们都知道是 ,但快速排序最坏情况(需要排序的数组为有序状态)下是 。看了别处的解释:行业默认的规定,因为快速排序一般情况下复杂度是 。
在计算时间复杂度的时候,可以忽略常数项和低阶项(比最大阶数低),前面解释过了。再强调一遍,这个是有前提的,即输入数据量足够大,常数和低阶项对最终时间复杂度的影响微乎其微。如果你的输入规模比较小,为了提高时间效率,可以权衡一下,例如 的时间复杂度可能会低于 。
运行规则如下所示:
- 如果 ,则
- ,其中 C 是一个正数
常见的时间复杂度( a 为常数,N 为输入数据的规模)
图片来源:https://blog.csdn.net/huihuishou_su/article/details/87861317
其中 和 级别的时间复杂度是无法在多项式时间内计算出来的,即NP问题,关于 NP 问题的介绍可见点我
还有一点需要注意 并不一定是以 2 为底的的对数,只要是以常数 C 为底的都可以表示为 ,可以忽略对底数的描述。
三、计算实例
1、
int a=10;
2、
int count=1;
while(count<n)
{
count=count*2;
}
3、
for(int i=0;i<n;i++)
{
int b=10
}
4、
//归并排序
void MergeSort(int *num,int low,int high)
{
if(low<high)
{
int mid = (low+high)/2;
MergeSort(num,low,mid);
MergeSort(num,mid+1,high);
Merge(num,low,mid,high);
}
}
//归并两个有序的数组
void Merge(int *num,int low,int mid,int high)
{
int i = low,j = mid+1,k=0;
int tmp[M]={0};
while(i<=mid&&j<=high)
{
if(num[i]>num[j]) tmp[k++] = num[j++];
else tmp[k++] = num[i++];
}
while(i<=mid) tmp[k++] = num[i++];
while(j<=high) tmp[k++] = num[j++];
for(i=0;i<k;i++)
{
num[i+low] = tmp[i];
}
}
5、
bool uniqueElements(int *A,int n)
{
for(int i=0;i<n-1;i++)
{
for(int j=i+1;j<n;j++)
{
if(A[i]==A[j])
{
return false;
}
}
}
return true;
}
更多例子参考:https://zhuanlan.zhihu.com/p/146490404
参考文献:《计算机算法设计与分析》、百度百科、算法复杂度分析的那些事、究竟什么是时间复杂度,怎么求时间复杂度,看这一篇就够了