时间复杂度

复杂度 可能对应的算法 备注
O(1) 位运算 常数级复杂度,一般面试中不会有
O(logn) 二分法,倍增法,快速幂算法,辗转相除法  
O(n) 枚举法,双指针算法,单调栈算法,KMP算法,Rabin Karp,Manacher's Algorithm 又称作线性时间复杂度
O(nlogn) 快速排序,归并排序,堆排序  
O(n^2) 枚举法,动态规划,Dijkstra  
O(n^3) 枚举法,动态规划,Floyd  
O(2^n) 与组合有关的搜索问题  
O(n!) 与排列有关的搜索问题  

在面试中,经常会涉及到时间复杂度的计算。当你在对于一个问题给出一种解法之后,面试官常会进一步询问,是否有更优的方法。此时就是在问你是否有时间复杂度更小的方法(有的时候也要考虑空间复杂度更小的方法),这个时候需要你对常用的数据结构操作和算法的时间复杂度有清晰的认识,从而分析出可优化的部分,给出更优的算法。

例如,给定一个已经排序的数组,现在有多次询问,每次询问一个数字是否在这个数组中,返回True or False.

  • 方法1: 每次扫描一遍数组,查看是否存在。
    这个方法,每次查询的时间复杂度是: O(n)

  • 方法2:由于已经有序,可以使用二分查找的方法。
    这个方法,每次查询的时间复杂度是: O(logn)

  • 方法3:将数组中的数存入Hashset。
    这个方法,每次查询的时间复杂度是: O(1)

可以看到,上述的三种方法是递进的,时间复杂度越来越小。

在面试中还有很多常见常用的方法,他们的时间复杂度并不是固定的,都需要掌握其时间复杂度的分析,要能够根据算法过程自己推算出时间复杂度。

T 函数推导法
我们介绍一种时间复杂度的推导方法:T函数推导法
比如二分法。二分法是每次通过 O(1) 的时间将规模为 n 的问题降低为规模为 n/2 的问题。
这里我们用 T(n) 来表示规模为 n 的问题在该算法下的时间复杂度,那么我们得出推导公式:

T(n)=T(n/2)+O(1)

我们来逐个说明一下这个公式的意义。

首先 T 代表的是 Time Complexity, nnn 代表的是问题规模(二分法里就是数组的大小)。
那么 T(n) 代表的就是:求处理问题规模为n的数据的时间复杂度是多少。注意这里是一个问句,不是一个答案。
T(n) 根据算法的不同可以是 O(n) 也可以是 O(nlogn) 或任何值,而 O(n) 就是 O(n)。

然后 O 代表的是时间复杂度。O(1) 就意味着,你大概用一个 if 语句,或者简单的加加减减,就可以完成。O 在这里的意思是数量级约等于。在 O 的世界里,我们只考虑最高项是什么,不考虑系数和常数项。比如:

O(100n)=O(n)
    O(n^2+n)=O(n^2)
    O(2^n+n^2+10)=O(2^n)

如何推导 T 函数
我们可以使用不断展开的方法进行推导:
T(n) = T(n/2) + O(1)
     = T(n/4) + O(1) + O(1)
     = T(n/8) + O(1) * 3
     = T(n/16) + O(1) * 4
     ...
     = T(1) + O(1) * logn
     = O(logn)

在时间复杂度的领域里,有如下的一些性质:

T(1)=O(1) // 解决规模为1的问题通常时间复杂度为O(1)。这个不100%对,但是99.9%的情况下都是如此。
    k∗O(n)=O(kn)
    O(n)+O(m)=O(n+m)
    
上面的方法,是采用 T 函数展开的方法,将二分法的时间复杂度最终用 O(...) 来表示。


空间复杂度

类似于时间复杂度,空间复杂度就是衡量算法运行时所占用的临时存储空间的度量。也是一个与问题规模n有关的函数。

算法所占用的空间主要有三个方面:算法代码本身占用的空间、输入输出数据占用的空间、算法运行时临时占用的空间。
其中,代码本身和输入输出数据占用的空间不是算法空间复杂度考虑的范围内,空间复杂度只考虑运行时临时占用的空间,又称为算法的额外空间(Extra space)。

临时占用的空间包括:

    • 为参数列表中形参变量分配的空间
    • 为函数体中局部变量分配的空间
      (如果是递归函数,需要将上述两部分占用空间的和乘以递归的深度,这是堆栈空间)

在递归函数中,除了变量和数组所开辟的临时空间以外,还有一个空间我们需要纳入考虑,就是递归时占用的栈空间(Stack)。

递归函数需要保存当前的环境,以便在递归返回的时候能够还原之前的现场。因此递归的深度越深,所要占用的栈空间越大。当空间超出一定范围的时候就会出现程序爆栈(Stack Overflow)的情况。

很多博客文章中会写堆栈空间与递归调用的次数成正比,这个是不完全正确的,应该是与递归的深度成正比(此处只讨论单线程)。
因为递归在返回到上一层的时侯,就会将本层的空间释放,因此占用的栈空间不会比最深的一次调用所占用的空间更多。

在面试中,很多时候面试官给出的问题会附带一个“不能使用多余的空间”这样的要求。很多时候这是在要求你的空间复杂度只能是O(1)的,也就是你只能开几个辅助变量,而不能开大数组。

其他常见的还有,分析一下你的算法空间复杂度,寻找空间复杂度更优的解法等。
一般来说,算法占用的时间和空间会是两个互相平衡的元素,有的时候我们牺牲空间来换取时间,有的时候我们牺牲时间来换取空间。

在面试中常见算法的空间复杂度:

快速排序: 最优:O(logn),最差:O(n)
    二分查找: O(1)
    最短路(Dijkstra)算法: O(V)  (V表示点集大小)

大部分的空间复杂度计算方法

累加下面两个部分的内容即是你代码的空间复杂度:

  1. 你的代码里开辟了多少新的空间(new 了多少新的内容出来)
  2. 你的递归深度 * 递归函数内部的参数和局部变量所占用的空间

以快速排序为例子

快速排序的思路如下:
1.选择一个基准元素,将原数组分为两部分,左边部分小于该元素,右边部分大于该元素。
2.分别递归处理左边和右边。

  • 最好情况:
    每次都能恰好将数组分成左右相同长度的两部分,需要的递归深度是:lgnlgnlgn,每次将数组分成两部分时,我们选择不使用辅助数组,在原数组上“就地”处理,所以每层的空间是O(1)
    因此总的复杂度是:O(logn)

  • 最差情况:
    每次都将数组分成长度差最大的两部分,即一边只有一个元素,其余的在另外一边,最大深度为:n, 因此空间复杂度为:O(n)

有些递归算法的空间复杂度是稳定的,不会退化,快排的递归深度与其每次选择的“基准值”有很大关系,因此存在退化的情况。


内存中的栈空间与堆空间

 
 

分解质因数

以 sqrt(n)为时间复杂度的算法并不多见,最具代表性的就是分解质因数了。

题目描述

http://www.lintcode.com/problem/prime-factorization/

具体步骤

  1. 记up=sqrt(n)作为质因数k的上界, 初始化k=2
  2. 当k<=up 且 n不为1 时,执行步骤3,否则执行步骤4。
  3. 当n被k整除时,不断整除并覆盖n,同时结果中记录k,直到n不能整出k为止。之后k自增,执行步骤2。
  4. 当n不为1时,把n也加入结果当中,算法结束。

几点解释

  • 不需要判定k是否为质数,如果k不为质数,且能整出n时,n早被k的因数所除。故能整除n的k必是质数。
  • 为何引入up?为了优化性能。当k大于up时,k已不可能整除n,除非k是n自身。也即为何步骤4判断n是否为1,n不为1时必是比up大的质数。
  • 步骤2中,也判定n是否为1,这也是为了性能,当n已为1时,可早停。

代码

 1 public List<Integer> primeFactorization(int n) {
2 List<Integer> result = new ArrayList<>();
3 int up = (int) Math.sqrt(n);
4
5 for (int k = 2; k <= up && n > 1; ++k) {
6 while (n % k == 0) {
7 n /= k;
8 result.add(k);
9 }
10 }
11
12 if (n > 1) {
13 result.add(n);
14 }
15
16 return result;
17 }

复杂度分析

最坏时间复杂度 O(sqrt{n})。当n为质数时,取到其最坏时间复杂度。
空间复杂度 O(log(n)),当n质因数很多时,需要空间大,但总不会多于O(log(n))


 

Leetcode Lect3 时间复杂度/空间复杂度的更多相关文章

  1. 时间复杂度&空间复杂度

    时间复杂度 参考链接: http://univasity.iteye.com/blog/1164707 空间复杂度 http://blog.csdn.net/booirror/article/deta ...

  2. 数据结构中常用的排序算法 && 时间复杂度 && 空间复杂度

    第一部分:数据结构中常用的排序算法 数据结构中的排序算法一般包括冒泡排序.选择排序.插入排序.归并排序和 快速排序, 当然还有很多其他的排序方式,这里主要介绍这五种排序方式. 排序是数据结构中的主要内 ...

  3. 计算时间复杂度&空间复杂度

    1.下面函数的复杂度是: long foo(long x){ if(x<2) return 1; return x*x*foo(x-1); } 解析: 当n>=2时 foo(n)=n^2* ...

  4. Leetcode Lect3 内存中的栈空间与堆空间

    内存中的栈空间与堆空间 我们通常所说的内存空间,包含了两个部分:栈空间(Stack space)和堆空间(Heap space) 当一个程序在执行的时候,操作系统为了让进程可以使用一些固定的不被其他进 ...

  5. Leetcode Lect3 二分法总结

    二分法模板 非递归版本: public class Solution { /** * @param A an integer array sorted in ascending order * @pa ...

  6. [Java初探外篇]__关于时间复杂度与空间复杂度

    前言 我们在前面的排序算法的学习中了解到了,排序算法的分类,效率的比较所使用到的判断标准,就包括时间复杂度和空间复杂度,当时因为这两个定义还是比较难以理解的,所以决定单独开一篇文章,记录一下学习的过程 ...

  7. 数据结构&算法(二)_算法基础之前传(递归、时间复杂度、空间复杂度、二分查找)

    什么是算法: 间而言之算法(Algorithm):一个计算过程,解决问题的方法 递归的两个特点: 调用自身 结束条件 递归示例: def func(x): : print("我的小鲤鱼&qu ...

  8. 【LeetCode题解】排序

    1. 排序 排序(sort)是一种常见的算法,把数据根据特定的顺序进行排列.经典的排序算法如下: 冒泡排序(bubble sort) 插入排序(insertion sort) 选择排序(selecti ...

  9. LeetCode每天一题之两数之和

    这个LeetCode刷题系列的博客权当是为自己记一下笔记吧.博客系列会从LeetCode的第一题开始刷,同时会从零开始学习[因为我就是零/(ㄒoㄒ)/~~].同时,如果有写错的地方,希望大佬们在评论区 ...

随机推荐

  1. python3-调用函数

    Python内置了很多有用的函数,我们可以直接调用. 要调用一个函数,需要知道函数的名称和参数,比如求绝对值的函数abs,只有一个参数.可以直接从Python的官方网站查看文档: http://doc ...

  2. selenium 自动化的坑(4)

    今天要讲的坑是....输入框有请求的. 问题是这样的,我们公司的业务有些输入框选项是联想的,这些联想都会发送请求,怎么办呢? 先 正常输入,然后强制等待几秒,最好多等会,谁知道第三方会不会有问题,这里 ...

  3. 027:for标签使用详解

    for标签使用详解: for...in... 标签: for...in... 类似于 Python 中的 for...in... .可以遍历列表.元组.字符串.字典等一切可以遍历的对象.示例代码如下: ...

  4. JavaScript用在哪里

    <script> 标签 在HTML,JavaScript代码必须插入<script> 和 </script>之间的. document.getElementById ...

  5. python学习笔记(十一)处理json

    json串就是字符串,json串里必须是双引号 d={'car':{'color':'red','price':100,'count':50}, '爱分叉':{'color':'red','price ...

  6. ClickOnce是什么?如何使用?

    ClickOnce是一种部署方式,主要解决C/S软件版本更新后,所有客户端都要重新安装的问题. 场景:假如,你的客户有1000个,你的服务端更新后,这1000个客户都要重新安装,难道要员工一个一个的电 ...

  7. 写一下SPFA和迪杰斯特拉的模版。。。第一次写博客,有错请提出哦!

    SPFA的模版 #include<bits/stdc++.h> using namespace std; queue <int> q; typedef pair <int ...

  8. 有关CSS的一些事

    看到两篇关于CSS的文章,总结的非常好.因为没有那个网站的账号,没法收藏转发,所以把链接贴在这里,分享给大家.这两篇文章对于初学CSS的人来说,总结得很精炼准确,而且通俗易懂.推荐~ 有关CSS的一些 ...

  9. 2019 年百度之星—初赛一 B题 Game

    题目链接 题意:最开始可以选择任意位置,在一个坐标轴上,依次走到一个区间里面,可以选择走一步两步,求最小步数. 思路:贪心,刚开始合并区间,确定初始位置以及方向.往右走肯定到左端点,往左走先到右端点, ...

  10. 170819-关于JSTL的知识点

    1.JSTL(JSP Standard Tag Library) [1] JSTL简介 > JSTL是JSP的标准标签库 > JSTL为我们提供了一些常用的标签,供我们日常开发使用(if ...