关于时间复杂度和空间复杂度的理解与应用

一. 为什么要进行时间复杂度分析

当我们有多种方法可以解决问题时,我们需要学习如何比较不同算法的性能,并选择最佳算法来解决特定问题。在分析算法时,我们主要考虑时间复杂度和空间复杂度。算法的时间复杂度根据输入长度来量化算法运行所需的时间。类似地,算法的空间复杂度将算法占用的空间或内存量量化为输入长度的函数。

时间和空间的复杂性取决于许多因素,例如硬件,操作系统,处理器等。但是,在分析算法时,我们不考虑任何这些因素。我们将只考虑算法的执行时间。在不同的硬件环境,测试同样一份代码其效果是不一样的。那么复杂度分析具有成本低,效率高的特点。

二. 归纳总结

根据笔者的归纳总结,在计算时间复杂度时,常常使用三种不同的函数符号来表示不同函数之间的限制。O符号经常用于表示渐进上限。对于给定的g(n),我们可以使用O(g(n))来表示存在正常数c和n0,这样0 <= f(n) <=cg(n) 对所有n>=n0都成立。Ω符号经常用于表示渐近下界。对于给定的g(n),我们可以用 Ω(g(n)) 来表示存在正常数c和n0,这样0 <=cg(n) <= f(n) 对于所有n>n0都成立。Θ符号经常用于表示渐近紧边界。对于给定的g(n),我们可以用Θ(g(n))来表示存在正常数c1,c2和n0,这样0 <=c1g(n)<=f(n)<=c2g(n)对所有n>n0都成立。为了方便理解,笔者附上三张用于显示时间复杂度的图。
在这里插入图片描述
在分析算法时,我们主要考虑O符号,因为它会给我们执行时间的上限,即最坏情况下的执行时间。计算O时要注意,我们将忽略低阶术语,因为对于大输入而言,低阶项将变得相对无关紧要。举个简单的例子,假设给出一个这样的函数 f(x) = x^2 + 2x +5. 那么它的时间复杂度就是 O(f(x))=O(x^2)

三. 时间复杂度的应用以及理解

接下来,笔者将描述在研究时间复杂度时遇到的一些问题,以及笔者对时间复杂度在程序算法应用上的理解。

func sum (n int) int{
	var sum = 0
	for i:=0;i<n;i++ {    // 需要执行 (n + 1) 次
		sum=sum+1    // 需要执行 n 次
	}
	return sum   //需要执行一次
}

先用笔者在用go语言写出的方法举一个简单的例子,如上述代码所示,for循环一共需要执行n+1次,因为当i=n-1时,循环体还在进行,并且当i++后,此时i=n,不符合i<n,则不进行循环内的内容,但是i=n时程序第三行还是执行了一次。因此循环体内的内容执行次数比for循环的执行次数少1。那么这个方法总共需要执行 (n+1) + n +1 = 2n + 2次。我们把算法需要执行的运算次数用输入大小n 的函数表示,即 T(n) 。存在常数 c 和函数 f(N),使得当 N >= c 时 T(N) <= f(N),表示为 T(n) = O(f(n)),此时符合了0 <= f(n) <=c*g(n)的条件。当 N >= 2 的时候,f(n) = n^2 总是大于 T(n) = n + 2 的,于是我们说 f(n) 的增长速度是大于或者等于 T(n) 的,也说 f(n) 是 T(n) 的上界,可以表示为 T(n) = O(f(n))。

那么当我们计算出程序的T(n),怎么求得算法的时间复杂度呢?

  1. 根据之前笔者归纳的信息,当T(n)是一个常数项c时,其对函数的增长速度影响不大,因此我们可以将这个时间复杂度设置为O(1)。
  2. 高次项对于函数的增长速度的影响比低次项来得高,因此当出现类似 f(x) = x^2 + 2x +5的函数,我们只取其最高项,忽略其低阶项,那么它的时间复杂度就是 O(f(x))=O(x^2)
  3. 忽略与最高阶相乘的常数。例如f(x)=5x^3 + 2x^2 +2时,我们可以把5给忽略,只提取最高次项,那么其时间复杂度就是O(x^3)

那么如何去计算T(n)呢,笔者在计算T(n)时发现以下方法并用自己的理解阐述以供读者参考:

  1. 根据循环体与循环时间复杂度相乘得到时间复杂度。就以此段代码为例,
func sum (n int) int{
	var sum = 0
	for i:=0;i<n;i++ {    // 需要执行 (n + 1) 次
		sum=sum+1    // 需要执行 n 次
	}
	return sum   //需要执行一次
}

循环体中sum = sum +1只占用了O(1)的时间复杂度,而for循环的循环次数为n+1,则for循环的时间复杂度为O(n),那么这一整个循环的时间复杂度为O(n*1)=O(n)。

  1. 在多个循环体中,则计算时间复杂度与第一条相似。
func sum (n int) int{
	var sum = 0
	for i:=0;i<n;i++ {
		for j:=0;i<n;j++ {
			sum=sum+1
		}
	}
	return sum
}

循环体中,sum=sum+1占用O(1)的时间复杂度,参数为j的循环的时间复杂度为O(n),并且参数为i的循环的时间复杂度为O(n),那么这段程序代码的时间复杂度为O(nn1)=O(n^2).

  1. 对于按照顺序执行的语句或者算法,总的时间复杂度等于其最大的时间复杂度。简单来说,就是计算出各个程序块的时间复杂度,取其中的最大值。
    还是用第二个方法中的程序代码, return sum的时间复杂度为O(1), 而此段代码段的最大时间复杂度为O(n^2),因此总的时间复杂度为 O(n ^ 2).

  2. 当遇到条件判断语句时,也是取其最大的时间复杂度为总时间复杂度。

func sumZero(n int) int {
	var sum = 0
	if n==1{
		sum=sum+1
	}
	if n%2==0 {
		temp := n / 2
		for i := 0; i <= temp-1; i++ {
			sum=sum+1
		}
		return sum
	}
	temp := (n-1) / 2
	for i:=0;i<=temp-1;i++ {
		sum=sum+1
	}
	sum=sum+1
	return sum
}

例如这段代码,第一次判断语句的时间复杂度为O(1),第二次判断语句的时间复杂度为O(n), for 循环中的时间复杂度为O(n),因此其最大的时间复杂度为O(n),则总的时间复杂度为O(n)。

  1. 计算非正常循环时,应该考虑其其他情况。
func print(n int) string{
	for i:=2;i<n;i++{
		i *= 2
		fmt.Println(i)
	}
}

假设循环次数为 t,则循环条件满足 2^t < n。
可以得出,执行次数t = log(2)(n),即 T(n) = log(2)(n),可见时间复杂度为 O(log(2)(n)),即 O(log n)。

  1. 求解使用递归算法时的时间复杂度
func plus(n int) int{
	if n<=1 {
		return 1
	}
	return plus(n-1) + plus(n-2) 
}

这里使用到了斐波那契数列,T(n) = T(n - 1) + T(n - 2)。用归纳证明法可以证明,当 n >= 1 时 T(n) < (5/3)^n,同时当 n > 4 时 T(n) >= (3/2)^n。因此其时间复杂度为 O((5/3)^n)。

四. 为什么要进行空间复杂度分析

空间复杂度是指算法在内存内所需要的存储空间的度量,一般是指除正常占用内存开销外的辅助存储单元规模。和时间复杂度类似,这样标记:S(n)=O(f(n))。对于一个算法,其时间复杂度和空间复杂度往往是相互影响的。当追求一个较好的时间复杂度时,可能会使空间复杂度的性能变差,即可能导致占用较多的存储空间;反之,当追求一个较好的空间复杂度时,可能会使时间复杂度的性能变差,即可能导致占用较长的运行时间。

五. 总结归纳

笔者认为在计算空间复杂度时,计算的方式与计算时间复杂度时颇为相似。输入数据所占空间只取决于问题本身,和算法无关,则只需分析除输入和程序之外得辅助变量所占额外空间。首先,明确一个概念,算法的存储量包括:程序本身所占空间,输入数据所占空间,辅助变量所占空间。

func sum (n int) int{
	var sum = 0
	for i:=0;i<n;i++ {
		for j:=0;i<n;j++ {
			sum=sum+1
		}
	}
	return sum
}

例如,在这段代码中算法中临时变量得个数与问题规模n无关,所以空间复杂度均为S(n) = O(1)。

func plus(n int) int{
	if n<=1 {
		return 1
	}
	return plus(n-1) + plus(n-2) 
}

运用递归实现时,调用fun函数,每次都执行一次判断语句。执行n次,空间复杂度O(n*1)=O(n)。

参考:
https://www.hackerearth.com/zh/practice/basic-programming/complexity-analysis/time-and-space-complexity/tutorial/
https://www.jianshu.com/p/f4cca5ce055a
https://www.cnblogs.com/zang1998/p/11552931.html
https://www.cnblogs.com/wonker/p/11238418.html
https://blog.csdn.net/tianxiaojie_blog/article/details/86607748
https://www.cnblogs.com/lanjianhappy/p/12079310.html
https://baike.baidu.com/item/%E7%A9%BA%E9%97%B4%E5%A4%8D%E6%9D%82%E5%BA%A6/9664257?fr=aladdin

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值