数据结构与算法 05 时间复杂度分析 → 函数调用&&最坏情况&&空间复杂度分析

2.4 时间复杂度分析 → 函数调用 && 最坏情况 && 空间复杂度分析


2.4.1 时间复杂度分析 → 函数调用

之前,我们分析的都是在单个函数内的,算法代码的时间复杂度。接下来我们 来分析一下 函数调用过程中时间复杂度。

  • 案例①:
public static void main(String[] args)
{
	int n = 100;
	for(int i = 0;i < n;++i)
	{
		show();
	}
}

private static void show(int i){
	System.out.println(i);
}

函数调用的时间复杂度分析 与 循环和判断 的分析时一样的,它们都应该 被忽略掉 外壳,而只 考虑 内部的 核心代码。所以 当我们 调用函数的时候,不会去 考虑 调用这个 函数 需要 多长时间,只会考虑 调用这个 函数,内部的代码 执行了 多少次而已。


2.4.2 最坏情况

从心理学角度讲,每个人对发生的事情都会有一个预期,比如看到半杯水,有人会说:哇哦,还有半杯水哦!但也有人会说:天哪,只有半杯水了。一般人处于一种对未来失败的担忧,而在预期的时候趋向做最坏的打算,这样即使最糟糕的结果出现,当事人也有了心理准备,比较容易接受结果。假如最糟糕的结果并没有出现,当事人会很快乐。

  • 算法分析也是类似的,假如有一个需求
  • 一个存储了 n 个随机数字的数组,要在其中查找出指定的数字
public int search(int num)
{
	int[] arr = {11,10,8,9,7,22,23,0};
	for(int i = 0;i < arr.length;++i)
	{
		if(num == arr[i])
		{
			return i;
		}
	}
	return -1;
}

上述代码会出现 以下几种情况:

  • 最好情况:

查找的第一个数字就是期望的数字,那么算法的时间复杂度 为 O(1)

  • 最坏情况:

查找的最后一个数字才是期望的数字,那么算法的时间复杂度为 O(n)

  • 平均情况:

任何数字查找的平均成本 是 O(n/2)

最坏情况是一种保障!!!在应用中,这是一种最基本的保障!因为如果在最坏的情况下,还能够正常的提供服务的话,那就说明该算法 是可以通过的!而我们提到的 算法执行时间其实指的都是 最坏情况下的 时间。


2.4.3 空间复杂度分析基本了解

  1. Java 基本数据类型内存的占用情况
    在这里插入图片描述
    特别强调更正:boolean 占用的 是 一位!并非一个字节(八位)。
  2. 计算机访问内存的方式都是一次一个字节
    在这里插入图片描述
  3. 一个引用(内存地址)需要 8 个字节来表示

例如:Date date = new Date() 则 date 这个变量 就需要 占八个字节 来做 表示。

  1. 创建一个对象,比如 new Date(),除了 Date 对象内部存储的数据(例如 年月日 等信息)占用的内存,该对象本身也有内存的开销,每个对象的自身开销是 16 个字节,用来 保存 对象的 头信息。

  2. 因为类中 会有 保存头信息的 内存开辟,所以 不够 8 个字节 开辟的成员,需要 根据 内存对齐,来补到 8 个字节。

public class A{
	public int a = 1;
}
通过 new A() 创建一个 对象的内存占用 如下:
	1.整型成员变量 a 占用 4 个字节
	2.对象本身占用 16 个字节

所以创建上述该对象 总共 需要 24 个字节,而不是 20 个字节!!!

  1. Java 中的 数组被限定为 对象,即 引用类型的 变量来进行存储。它们 一般都会 记录自身的长度,所以 就会 出现 额外的内存占用,一个 基本数据类型的数组,一般需要 24 个字节的头信息(16 个 字节 保存 对象自身的开销,4 个 字节 用于 保存 长度 以及 4 个字节 补充!) 也就是说 一个 基本数据类型的数组,在不存储任何数据的时候,就有 24 个字节的开销了。

2.4.4 头信息 是什么 ?

头信息包括:

  1. 对象头中的Mark Word(标记字)主要用来表示对象的线程锁状态,另外还可以用来配合GC、存放该对象的hashCode(即该对象所在的内存地址哈希值)

  2. Klass Word是一个指向方法区中Class信息的指针,意味着该对象可随时知道自己是哪个Class的实例。(即指向的是 自身的类模板)

  • 32 位时

Mark Word 4 位
Klass Word 4 位

  • 64 位时

Mark Word 8 位
Klass Word 4 位(指针压缩)


Mark Word 8 位
Klass Word 8位


2.4.5 算法的空间复杂度

了解了 Java 的内存 最基本的机制,就能够 有效帮助我们 估计 大量程序的 内存使用情况。

算法的空间复杂度计算公式 记作: S(n) = O(f(n)) ,其中 n 为输入规模, f(n) 为语句 关于 n 所占 存储空间的函数。

  • 案例:

对指定的 数组元素 进行反转,并返回反转的 内容

  • 解法①
public static int[] reverse1(int[] arr)
{
	int n = arr.length;//申请 4个字节
	int temp;//申请 4个 字节
	for(int start=0,end = n-1;start <= end;start++,end--)
	{
		temp = arr[start];
		arr[start] = arr[end];
		arr[end] = temp;
	}
	return arr;
}

f(n) = 8
O(f(n)) = 1 (空间复杂度 为 1,常数复杂度!!)

  • 解法②
public static int[] reverse2(int[] arr)
{
	int n = arr.length;//4
	int[] temp = new int[n];//4*n + 24 
	for(int i = n - 1;i >= 0;++i)
	{
		temp[n-1-i] = arr[i];
	}
	return temp;
}

f(n) = 4n + 24 + 8
S(n) = O(f(n)) = n

根据大O推导法则,算法①的空间复杂度 为O(1),算法②的空格键复杂度为O(n),所以从空间占用的角度来看,算法①要比算法②要好很多。


由于 Java 中有 内存垃圾回收机制(GC),并且 JVM 对程序的内存占用 也有 优化(例如 即时编译),我们 无法精确的 评估 一个 Java 程序的 内存占用情况,但是 了解了 Java 的基本内存占用,使我们可以对 Java 程序的内存占用情况 进行估算。


由于现在的计算机设备内存一般都比较大,基本上个人计算机都是 8G 起步,大 的 可以 达到 64G,所以 内存占用 现在已经不是我们 算法的 瓶颈了,普通的情况下,我们愿意 直接 说 算法的时间复杂度 来衡量 一个 算法的 优越性!

但是,如果你做的 程序 是 嵌入式开发,尤其是一些传感器设备上的内置程序,由于这些设备的内存很小,一般为 几 KB,这个时候 对 算法的 空间复杂度 就 有 很大的要求了!!

但是 一般做 Java 开发的,都是 基于服务器的,而 你会发现 现在 所有的东西 都离不开 服务器这方面!!(因为 数据的传输呀 ~ ~)

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值