时间复杂度

为什么写这篇文章呢?网上有文章讲解到时间复杂度,很详细,但我看了之后总感觉欠缺的什么,于是我就写了这篇文章。

时间复杂度

普遍的时间复杂度

通常情况下,代码的时间复杂度能用到的有 O ( n ) , O ( 1 ) , O ( l o g n ) , O ( n l o g n ) , O ( n 2 ) , O ( n k ) , O ( ( n ) ) , O ( 2 n ) , O ( n ! ) 等 O(n),O(1),O(logn),O(nlogn),O(n^2),O(n^k),O(\sqrt(n)),O(2^n),O(n!)等 O(n),O(1),O(logn),O(nlogn),O(n2),O(nk),O(( n)),O(2n),O(n!)

简单分析次方级时间复杂度

正常情况下,一个for等于次方加一,比如以下代码:

	for(...)
		for(...)
			for(...)

因为是三个for,所以可以直接判断是 O ( n 3 ) O(n^3) O(n3)
当然,还有一种特殊情况:

int i, j = 1;
for (i = 1; i <= n; i++)
{
	while (j <= n) j++;
}

虽然是两重循环,可以直接定义 O ( n 2 ) O(n^2) O(n2),但是注意,内层循环和外层循环的总遍历次数不超过O(n + n),也是属于O(n)级别的,当然O(n)我们通常叫线性复杂度
这是一种简单直观的分析时间复杂度的方法,只能做到大概的描述时间复杂度,更细的难以分析到。

logn判断

刚开始接触 O ( l o g n ) O(logn) O(logn)时,很难懂,为什么log下面没有底数?正常数学中对数肯定包含底数的,

一般地,如果a(a大于0,且a不等于1)的b次幂等于N(N>0),那么数b叫做以a为底N的对数,记作 l o g a N = b log_aN=b logaN=b,读作以a为底N的对数,其中a叫做对数的底数,N叫做真数。-----------来源于网络
对数

可当你真正深入的去计算(我选择一个一个枚举)的时候,你会发现,它的底数为10,?!,当你再算找一个例子去算,底数又成2了?!底数竟然能变化?数学崩了》》。
当然,其实不是数学崩了,只是理解的方式不对,log下面无底数,表示的意义其实是:(我也不知道底数是几)。很离谱,可是 l o g n logn logn无论底数多少,通常情况下它都是有范围的,比如说 O ( l o g n ) O(logn) O(logn)是在O(1)到 O ( ( n ) ) O(\sqrt(n)) O(( n))之间的,管你多少,只要在这个范围内,就行了。
通常,底数都是2或者10。
那么怎么判断时间复杂度是不是或有没有logn呢?很好理解,logn时间复杂度都不会很大,如
1e18来个 l o g 10 log_{10} log10直接变成18,所以我们可以直接暴力枚举乱搞判断(我不支持)。
当然还有个更高效的方法。
据我观察,大多数logn都是含有分治思想或大化成小思想的,如分治算法,二分/二分查找,归并排序,快速排序,还有一部分是枚举,但是时间复杂也是logn的,这些枚举通常枚举次数很小或者枚举的跨度很大。如埃氏筛法内层循环:

for(int j=i+i;j<=N;j+=i)

虽是暴力,但时间复杂度是n/i的,大概也就logn左右。
反正只要枚举次数少,大概率就是logn。
提到logn,肯定少不了一个重要的东西:调和级数
调和级数是 n / 1 + n / 2 + n / 3 + n / 4 + . . . n / n n/1+n/2+n/3+n/4+...n/n n/1+n/2+n/3+n/4+...n/n,这样的大小是nlogn的,怎么证明呢?(我也不会(其他大佬的证明过程))
调和级数其实也没太大难度,大概就是当个公式用用,若碰到了某些题,是调和级数差不多的,那就能用调和级数做。

回溯或递归(递推)函数的T(n)

在我们正常的认知里,有些人喜欢用函数,用模块化的函数,但我们可以做个调查,在使用递归或递推还或者回溯(尤其是回溯),时间复杂度会比较高,为什么呢?因为调用函数也是需要时间复杂度的。正常的模块化函数的时间复杂度是很低的,因为就调用最多几次。但是回溯,递归,递推的函数中,要调用很多函数,或者自我调用。这就会使时间复杂度变高。
比如说,在某些函数中,调用次数可能为n次, n 2 n^2 n2次,甚至更多。而这些就构成了我们的阶乘时间复杂度,2的次方的时间复杂度等。所以在写代码时,就会出现比如说TLE的情况,但时间复杂度级别没问题,那就得考虑是不是写了某些函数,调用次数过高了,导致整体时间复杂上升了。

卡常(不能忽视的常数)

当然,在写代码时,你可能会发现时间复杂度没问题,但TLE,这种情况就叫卡常(上面的函数调用次数太多TLE也算卡常)。
卡常顾名思义,就是在常数级上卡住了,比如说你代码的级别是O(n^2)的,但是算算总时间复杂度是 O ( n 2 + n ( n ) + n l o g n + n + n + n + n + l o g n + l o g n + l o g n ) O(n^2+n\sqrt(n)+nlogn+n+n+n+n+logn+logn+logn) O(n2+n( n)+nlogn+n+n+n+n+logn+logn+logn),这么大的常数,于是就TLE了。
比如以下代码:

	for(int i=1;i<=n;i++)
	{
		ans+=1;
		ans+=1;
		ans+=1;
		ans%=mod;
		ans%=mod;
		for(int j=1;j<=n;j++)
		{
		ans=ans*j+ans%mod+ans*i+i*1LL*j%mod;
		ans%=mod;
		}
	}

假设 O ( n 2 ) O(n^2) O(n2)是允许的时间复杂度,可是实际上的时间复杂度在 O ( n 2 ) O(n^2) O(n2)上加上了许多系数和许多常数,所以这份代码会TLE。

后记

以上就是一些普遍的时间复杂度(也有不普遍的),当然,如果有遗漏或你有更好的建议,都可以提出来哦。

  • 6
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值