02、Java 数据结构:时间复杂度与空间复杂度

1 场景理解

1.1 场景1

给大黄一条长10寸的面包,大黄每3天吃掉1寸,那么吃掉整个面包需要几天?

  • 答案自然是 3 X 10 = 30天。
  • 如果面包的长度是 N 寸呢?
  • 此时吃掉整个面包,需要 3 X n = 3n 天。
  • 如果用一个函数来表达这个相对时间,可以记作 T(n) = 3n。

1.2 场景2

给大黄一条长16寸的面包,大黄每5天吃掉面包剩余长度的一半,第一次吃掉8寸,第二次吃掉4寸,第三次吃掉2寸…那么大黄把面包吃得只剩下1寸,需要多少天呢?

  • 这个问题翻译一下,就是数字16不断地除以2,除几次以后的结果等于1?这里要涉及到数学当中的对数,以2位底,16的对数,可以简写为log16。因此,把面包吃得只剩下1寸,需要 5 X log16 = 5 X 4 = 20 天。
  • 如果面包的长度是 N 寸呢?
  • 需要 5 X logn = 5logn 天,记作 T(n) = 5logn。

1.3 场景3

给大黄一条长10寸的面包和一个鸡腿,大黄每2天吃掉一个鸡腿。那么大黄吃掉整个鸡腿需要多少天呢?

  • 答案自然是2天。因为只说是吃掉鸡腿,和10寸的面包没有关系 。
  • 如果面包的长度是 N 寸呢?
  • 无论面包有多长,吃掉鸡腿的时间仍然是2天,记作 T(n) = 2。

1.4 场景4

给大黄一条长10寸的面包,大黄吃掉第一个一寸需要1天时间,吃掉第二个一寸需要2天时间,吃掉第三个一寸需要3天时间…每多吃一寸,所花的时间也多一天。那么大黄吃掉整个面包需要多少天呢?

  • 答案是从1累加到10的总和,也就是55天。
  • 如果面包的长度是 N 寸呢?
  • 此时吃掉整个面包,需要 1+2+3+…+ n-1 + n = (1+n)*n/2 = 0.5n^2 + 0.5n。
  • 记作 T(n) = 0.5n^2 + 0.5n。

1.5 代码实现

上面所讲的是吃东西所花费的相对时间,这一思想同样适用于对程序基本操作执行次数的统计。刚才的四个场景,分别对应了程序中最常见的四种执行方式

场景1: T(n) = 3n,执行次数是线性的

void eat1(int n){
    for(int i=0; i<n; i++){;
        System.out.println("等待一天");
        System.out.println("等待一天");
        System.out.println("吃一寸面包");
    }
}

场景2: T(n) = 5logn,执行次数是对数的

void eat2(int n){
   for(int i=1; i<n; i*=2){     
       System.out.println("等待一天");
       System.out.println("等待一天");
       System.out.println("等待一天");
       System.out.println("等待一天");
       System.out.println("吃一半面包");
   }
}

场景3: T(n) = 2,执行次数是常量的

void eat3(int n){
   System.out.println("等待一天");
   System.out.println("吃一个鸡腿");
}

场景4: T(n) = 0.5n^2 + 0.5n,执行次数是一个多项式

void eat4(int n){
   for(int i=0; i<n; i++){   
       for(int j=0; j<i; j++){
           System.out.println("等待一天");  
       }
       System.out.println("吃一寸面包");   
   }
}

2 时间复杂度

2.1 渐进时间复杂度

  • 有了基本操作执行次数的函数 T(n),是否就可以分析和比较一段代码的运行时间了呢?还是有一定的困难
  • 比如算法 A 的相对时间是T(n)= 100n,算法 B 的相对时间是 T(n)= 5n^2,这两个到底谁的运行时间更长一些?这就要看 n 的取值了。所以,这时候有了渐进时间复杂度(asymptotic time complectiy)的概念
  • 官方的定义如下:若存在函数 f(n),使得当 n 趋近于无穷大时,T(n) / f(n) 的极限值为不等于零的常数,则称 f(n) 是 T(n) 的同数量级函数。记作 T(n) = O(f(n)),称 O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。渐进时间复杂度用大写 O 来表示,所以也被称为大 O 表达式

2.2 从基本操作执行次数推导出时间复杂度

  • 如果程序的运行次数和要处理的量n的大小没有关系,用常数1表示;O(1)
  • 如果程序的运行次数和要处理的量n的大小有关系,只保留关系函数中的最高阶项; O(n^2)
  • 如果最高阶项存在,则省去最高阶项前面的系数。 O(n^2)

2.3 两种方法来计算

  • 事后统计:写出代码,计算时间,不推荐。
  • 事前分析:比较所有语句执行的次数总和,为了方便,仅比较它们的数量级就行
    在这里插入图片描述

有时候复杂度会受其他数据的影响,会有最坏时间复杂度、平均时间复杂度、最好时间复杂度,一般情况下,只考虑最坏时间复杂度和平均时间复杂度

对于复杂的算法,可以将其分为几个容易估算的部分,然后利用大 O 加法法则和乘法法则,计算算法的复杂度:

  • 加法法则取时间复杂度最大的一个
  • 乘法法则几个计算出来的最后时间复杂度相乘
    在这里插入图片描述

2.4 四个场景的时间复杂度分析

场景1:
T(n) = 3n
最高阶项为3n,省去系数3,转化的时间复杂度为:T(n) = O(n) 大 O 线性阶
在这里插入图片描述
场景2:
T(n) = 5logn
最高阶项为5logn,省去系数5,转化的时间复杂度为:T(n) = O(logn) 大 O 对数阶
在这里插入图片描述

场景3:
T(n) = 2
只有常数量级,转化的时间复杂度为:T(n) = O(1) 大 O 常数阶
在这里插入图片描述

场景4:
T(n) = 0.5n^2 + 0.5n
最高阶项为 0.5n^2,省去系数0.5,转化的时间复杂度为:T(n) = O(n^2) 大 O 平方阶
在这里插入图片描述

2.5 大 O 表达式的优劣

大O表达式算法的好坏
O(1)最好
O(logn)比较好
O(n)良好
O(n^2)不好
O(n^3)很不好
O(2^n)很很不好
O(n!)最不好

例子:

  • 算法A的相对时间规模是 T(n)= 100n*100,时间复杂度是 O(n)
  • 算法B的相对时间规模是 T(n)= 5n^2,时间复杂度是 O(n^2)
  • 随着输入规模 n 的增长,两种算法谁运行更快呢?
    在这里插入图片描述
    从表格中可以看出,当 n 的值很小的时候,算法 A 的运行用时要远大于算法 B;当 n 的值达到1000左右,算法 A 和算法 B 的运行时间已经接近;当 n 的值越来越大,达到十万、百万时,算法 A 的优势开始显现,算法 B 则越来越慢,差距越来越明显

3 空间复杂度

空间复杂度和时间复杂度很类似,当一个算法的空间复杂度为一个常量,即不随被处理数据量 n 的大小而改变时,可表示为 O(1);当一个算法的空间复杂度与以2为底的 n 的对数成正比时,可表示为 O(log2n);当一个算法的空间复杂度与 n 成线性比例关系时,可表示为O(n)…

4 时间复杂度和空间按复杂度关系

4.1 关系

程序的设计中要不就是时间换空间,要不就是用空间去换时间。并且时间和空间是可以进行相互转化的:

  • 对于执行的慢的程序,可以通过消耗内存(即构造新的数据结构)来进行优化
  • 消耗内存的程序,也可以多消耗时间来降低内存的消耗

4.2 例子

//时间换空间
int a = 5;
int b = 10;
a = a+b;//得到a值为15
b = a-b;//得到b值为5
a = a-b;//得到a值为10

//空间换时间
int c = 5;
int d = 10;
int e = c;//得到e为5
c= d;//得到c值为10
d= e;//得到d值为 

结论:

  • 第一个 a 和 b 互换值的算法:总共进行了3次加减运算和三次赋值运算,能够把 a 和 b 的值进行互换,没有开辟多余的内存空间
  • 第二个 c 和 d 互换的时候,多开辟了一个内存空间存储 e,但是这样只需要进行三次赋值运算就可以把 c 和 d 的值进行互换
  • 所以第一个算法空间效率高,时间效率低,第二个算法空间效率低,时间效率高

4.3 程序中的应用

在程序当中,请求分页,请求分段,都属于用时间去换空间。在项目当中使用各种缓存技术,都属于利用空间去换时间。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

玄天灵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值