数据结构与算法二最好、最坏、平均、均摊时间复杂度

前言

知识让生活更具能量。希望我们在以后学习的路上携手同行。您的点赞、评论和打赏都是对我最大的鼓励。一个人能走多远要看与谁同行,希望能与优秀的您结交。

我们在上一章, 主要是介绍了一下时间复杂度的分析,接下来接着跟大家聊一下时间复杂度的知识点。

最好、最坏情况时间复杂度

我们可以尝试分析下面的代码

//n表示数据array的长度
int find(int[] array,int n, int x){
	int i = 0;
	int pos = i;
	for(; i<n ; ++i){
		if(array[i] = x){
			pos = i;
		}	
	}
	return pos;
}

这段代码的功能是:在一个无序是的数组中找出x所在的位置。如果没有找到,就返回-1。
我们可以用让一章的内容分析出,这段代码阶位最高的为for循环中的代码,代码复杂度为O(n)。

大家可以看出,这段代码的效率不是很高,我们是可以对代码的效率进行优化的。如果我们中途找到这x的值就可以直接退出for循环。

int find(int[] array,int n, int x){
	int i = 0;
	int pos = i;
	for(; i<n ; ++i){
		if(array[i] = x){
			pos = i;
			break;
		}	
	}
	return pos;
}

这个时侯,我们就不可以用O(n)表示这段代码的时间复杂度了。

因为,要查找的变量x可能出现在数组的任意位置。如果数据中的第一个元素正好是要查找的变量x,那就不需要继续遍历剩下的n-1个数据了,那时间复杂度为O(1), 如果x所在的位置为最后一个元素,那么这段代码的时间复杂度为O(n)。
这个时间我们要引入新的概念最好时间复杂度,最坏时间复杂度。上面第一种情况就对应我们最好时间复杂度,第二情况就对我们最坏时间复杂度。

平均时间复杂度

因为出现最好时间复杂度和最坏时间复杂度的情况不是很大, 我们引入了一个新的概念,平时时间复杂度。

我们就来分析一下刚才的例子,要查找的变量x在数组中的位置,有n+1种情况:在数组的0~n-1位置中和不在数组中。我们把每种情况下,需要遍历的元素个数累加起来,然后在除以n+1,
就可以得到需要遍历的元素个数的平均值: 1 + 2 + . . . . + n + n n + 1 = n ( n + 3 ) 2 ( n + 1 ) \frac{1+2+ ....+n+n}{n+1}=\frac{n(n+3)}{2(n+1)} n+11+2+....+n+n=2(n+1)n(n+3)

时间复杂度的大O表示法中,可以省略掉系数、低阶、常量。把公式简化之后得到平均时间复杂度为O(n)。这并不是最终的答案,我们还要将概率加入到中才可以得到最终的结果。

我们假设x在数组中与不在数组中的概率都是1/2。另外,要查找的数据出现在0~n-1这n个位置的概率也是一样的,为1/n。根据概念乘法法则,x数据出现在``0~n-1的任意位置的概率为1/(2n)。
我们将概率加到上面的公式中,那平均时间复杂度为在这里插入图片描述
这个值就是概率论中的加权平均值,也叫作期望值。
引入概率之后,前面那段代码的加权平均值为(3n+1)/4。用大O表示法来表示,去掉系数和常量,最终结果为O(n)。

均推时间复杂度

我们先看一下代码

int[] array = new int[n]
int count =0 ;
void insert(int val){
	if(count == array.length()){
		int sum = 0;
		for(int i = 0; i<array.length(); ++i){
			sum = sum + array[i];
		}
		array[0] = sum;
		count = 1;
	}
	array[count]=val;
	++count;
}

这段代码的实现了一个往数据中插入数据的功能。当数组满了之后,也就是代码中的count==array.length()时,我们用for循环遍历数组求和,并清空数组,将求和之后的sum值放到数组中的第一个位置,然后再将新的数据插入。但如果数组一开始就有空闲空间,则直接将数据插入数组。

我们先使用上面的三种复杂度分析法分析这段代码:
最好的情况下,数组中有空闲空间,我们只需要将数据添加到数据即可,所以最好情况时间复杂度为O(1)。
最坏的情况下,数组中没有空闲空间了,我们需要先做一次遍历求和,然后再将数据插入,所以最坏情况时间复杂度为O(n)。
那平均时间复杂度是多少呢?(重点)
假设数组的长度是n,根据数据插入的位置不同,我们可以分为n种情况 ,每种情况的时间复杂度都为O(1)。除此之外,还有一种“额外”的情况,就是在数组没有空闲空间时插入一个数据,这个时间复杂度为O(n)。而且 ,这n+1种情况发生的概率一样,都是1/(n+1)。所以,根据加权平均的计算方法,我们求得的平均时间复杂度为 1 ∗ 1 n + 1 + 1 ∗ 1 n + 1 + . . . . . + n ∗ 1 n + 1 = O ( 1 ) 1*\frac{1}{n+1} +1*\frac{1}{n+1}+.....+n*\frac{1}{n+1} = O(1) 1n+11+1n+11+.....+nn+11=O(1)

我们通过对比前面两个例子find() 和insert()。会发现他们两个不同点。
首先,find()方法只有在特殊的情况下复杂度才为O(1)。但insert()方法在大部分情况下,时间复杂度都为O(1)。只有在特殊的情况下,复杂度才为O(n)。
第二个不同点,对于insert()方法,时间复杂度O(1)和时间复杂度O(n)是规律出现的。一般一个O(n)之后就跟着n-1个O(1)操作。
针对这种情况,我们可以使用均推分析法,看下面的分析:
每一次O(n)的插入操作,都会跟着n-1次O(1)的插入操作,所以把耗时多的那次操作均推到接下来的n-1次耗时少的操作上,均推下来, 这一组连续操作的均摊时间复杂度就是O(1)。

规律: 对一个数据结构进行一组连续操作中,大部份情况下时间复杂度都很低,只有个别情况下时间复杂度比较高,而且这些操作之间存在前后连贯的时序关系 ,这个时间,我们就可以将这一组操作放在一块儿分析,看是否能将较高时间复杂度那次操作的耗时,平摊到其他那些时间复杂度比较低的操作上

总结:
在这两篇文章中,我们主要学习了时间复杂度的几种分析方法。 大O时间复杂度分析法中平均,均摊分析最难,但是也最不常用。 多多练习练习分析分析,应该就会了。

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值