学习数据结构与算法-01时间复杂度的计算

关于斐波那契数列的想法

在大一的时候,初学c语言,我们就学过了关于斐波那契数列的计算,但是当时学的递归的方法计算起比较大的数值n,电脑会跑很久甚至都跑不动。

这次就用一个优化后的计算方法与递归方法作对比,通过一些例子学会计算时间复杂度之后,计算这两个算法的时间复杂度,看看算某些数值时需要用时多久。

两种算法演示效果

递归方法

public static int fib1(int n){//递归方法
		if(n<=1) return n;
		return fib1(n-1)+fib1(n-2);
	}

递归的方法很简单,就是第n个斐波那契数是n-1个斐波那契数加上n-2个数,一直递归调用直到n=1与n=0时停下。
在这里插入图片描述
可以里看出,这里面存在着许多重复的调用,比如计算fib(5)时,fib(2)就调用了3次,这样大大不利于计算机的计算效率。
优化的算法

public static int fib2(int n){//优化的方法
		if(n<=1) return n;
		int first = 0;
		int second = 1;
		for (int i = 0; i < n-1; i++) {
			int sum = first +second;
			first = second;
			second  = sum;
		}
		return second;
	}
 *0 1 2 3 4 5 6 7  8//这里是n
 *0 1 1 2 3 5 8 13 21//这是n对应的结果

每次结果都是两个数相加,当次的是上一次计算中的second加上这次的first。
我们设定最终输出的值是second,那么每一次计算时sum=first+second,sum用来存计算结果,first=second,这时候的second就应该成为下一次计算的first,那么second=sum,输出这次计算结果。
为了方便对比运行速度,我们把两个个方法的运行时间显示出来。
下面展示完整代码:
basic01.java

package com.hgq;
import com.hgq.TimeTool.Task;
/**
 * @author 89241
 *0 1 2 3 4 5 6 7  8
 *0 1 1 2 3 5 8 13 21
 *斐波那契数列
 */
public class basic01 {
	public static int fib1(int n){//递归方法
		if(n<=1) return n;
		return fib1(n-1)+fib1(n-2);
	}
	public static int fib2(int n){//优化的方法
		if(n<=1) return n;
		int first = 0;
		int second = 1;
		for (int i = 0; i < n-1; i++) {
			int sum = first +second;
			first = second;
			second  = sum;
		}
		return second;
	}
	public static void main(String[] args) {//匿名类
		// TODO 自动生成的方法存根
		final int n=46;
		TimeTool.check("fib1", new Task(){
			public void execute(){//要执行的语句写在里面 
				System.out.println(fib1(n));
			}
		});	
		TimeTool.check("fib2", new Task(){
			public void execute(){
				System.out.println(fib2(n));
			}
		});
	}
}

TimeTool.java

package com.hgq;

import java.text.SimpleDateFormat;
import java.util.Date;

public class TimeTool {
	private static final SimpleDateFormat fmt = new SimpleDateFormat("HH:mm:ss.SSS");
	public interface Task{
		void execute();
	}
	public static void check(String title,Task task){
		if(task == null)return;
		title = (title == null)? "" :("【" + title +"】");
		System.out.println(title);
		System.out.println("开始:"+ fmt.format(new Date()));
		long begin = System.currentTimeMillis();
		task.execute();
		long end = System.currentTimeMillis();
		System.out.println("结束:"+ fmt.format(new Date()));
		double delta =(end - begin)/1000.0;
		System.out.println("耗时"+ delta+"秒");
		System.out.println("------------------");
	}

}

演示结果
在这里插入图片描述
可以看出优化后的算法几乎不耗时,但是递归算法却花了将近13秒,那么这是为什么呢,怎么去判断它的耗时程度呢?这就需要掌握时间复杂度的计算问题了。

时间复杂度计算例子

时间复杂度大体意思是程序需要被执行多少次,或者说是执行的时间。
例1:

在这里插入图片描述
这里面if判断,虽然有三句,但是每次只挑其中一句,所以算1次,for循环中,int i=0算一次,判断i<4要执行4次,i++也要执行4次,那么相对应的输出语句也要4次,最后总的就是14次。
O(1)。

然后我们了解一下大O表示法:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
效率对比:
数据规模较小时:
在这里插入图片描述
较大时:
在这里插入图片描述
所以这里的时间复杂度14->O(1)。
又因为只定义了int i,所以空间复杂度O(1).

例2:
在这里插入图片描述
这里同上,i=0算1次,剩下的每个都是n次,共3n,加起来就是3n+1,
用大O表示法就是:O(n)。
只定义了int i,所以空间复杂度O(1).

例3:
在这里插入图片描述
外循环:1+n+n,内循环:n*(1+3n),总的:3n^2+3n+1
时间复杂度O(n^2)。

例4:
在这里插入图片描述
外循环:1+2n,内循环:n*(1+15*3),总:48n+1
O(n)。

例5:
在这里插入图片描述

while循环类似if,我们直接算里面的语句执行的次数。如n=8,可以执行3次,n=16,可以执行4次,所以次数=log2(n)。
O(logn)

例6:
在这里插入图片描述
类似的,log5(n)
O(logn)

例7:
在这里插入图片描述
外循环:1+2log2(n),内循环:log2(n)(1+3n),总:2nlog2(n)+3*log2(n)+1
O(nlogn+logn)->O(nlogn) (因为低阶需要省略掉)。
比如n+logn->O(n)(这里面logn更低阶)。

例8:用来说明空间复杂度:
在这里插入图片描述
前3个都是定义常数,但后面定义了数组,n项,所以空间复杂度O(n)。

回到之前的两个算法

  1. 计算改良的算法的时间复杂度为:O(n)。
  2. 对于递归算法里的,每调用一次fib1就执行一次加法,所以要看最终调用了多少次这个函数,而前面给出了调用示例图,计算fib1(5),总调用1+2+4+8=15=2n-1-1= 0.5*2n-1,所以时间复杂度为O(2n).
  3. 回看前面的每个时间复杂度的曲线可以看出,这两个算法的时间复杂度在数据为几十时就已经体现出了巨大的差异。
  4. 下面来用实际例子大概说一下两个算法需要的时间:
    在这里插入图片描述

所以算法好的话,能够节省非常多的时间。

平常开发时,可以在空间和时间的资源中权衡,选取最优。

补充:
在这里插入图片描述
这样的程序要考虑两个循环,最后时间复杂度为O(n+k)。

学会了这题就可以去leetcode-cn.com里去练习题目了,509题。

注:这是我用来记录学习数据结构与算法过程的博客,学习的材料是小码哥的恋上数据结构与算法,本文中的一些图片也是来源于他的教学。后续会继续发布后面的学习笔记。如有发现错误欢迎指出,下次上线一定修改,希望一起进步!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值