本文首发公众号:架构精进​,排版比较清晰。

经常有同学在 LeetCode 的题解中问解法的复杂度是多少。作为一个懒人,我一直在「逃避」这个问题,毕竟这东西听起来就这么「复杂」。

但本着对题解认真负责的态度(心虚),我想趁此机会做一个总结。下面我将通过一些较为经典的算法题聊一聊几种常见的时间复杂度。

什么是时间复杂度?

算法的时间复杂度(Time complexity)是一个函数,用于定性描述算法的运行时间。

提出时间复杂度的目的是:分析与比较完成同一个任务而设计的不同算法

分析算法的结果意味着算法需要的资源,虽然有时我们关心像内存,通信带宽或者计算机硬件这类资源,但是通常我们想要度量的是计算时间。一般来说,通过分析求解某个问题的几种候选算法,我们可以选出一种最有效的算法。这种分析可能指出不止一个可行的候选算法,但是在这个过程中,我们往往可以抛弃几个较差的算法。
——《算法导论》

大 O 符号

时间复杂度通常用 大 O 符号(Big O notation)表示。大 O 符号 又被称为渐近符号,是用于描述函数 渐近行为。

举个例子,假设我们解决一个规模为 n 的问题要花费的时间为 T(n)T(n):

T(n)=4n2−2n+2T(n)=4n2−2n+2

当 n 不断增大时,n2n2 开始占据主导地位,而其他各项可以被忽略,写作 T(n)=O(n2)T(n)=O(n2)。因此时间复杂度可被称为是 渐近 的。

常见复杂度比较

常见时间复杂度比较

常数时间

若算法 T(n)T(n) 的上界与输入大小无关,则称它具有常数时间,记作 T(n)=O(1)T(n)=O(1)。

常见的例子有:

  • 访问数组中的单个元素
  • 哈希表

别被循环所迷惑

例如这道题 有效的数独,需要在 9x9 的格子中判断数独是否有效。

思路:把行、列和小正方形区域出现的数字用哈希表记录下来,在遍历过程中只要判断数字是否在这三个范围出现过就行了,如果出现过就返回 False

题解如下:

class Solution(object):def isValidSudoku(self, board):""":type board: List[List[str]]:rtype: bool"""row = [{} for _ in range(9)]col = [{} for _ in range(9)]area = [{} for _ in range(9)]area_index_dict = {0: {0: 0, 1: 1, 2: 2},1: {0: 3, 1: 4, 2: 5},2: {0: 6, 1: 7, 2: 8}}for i in range(9):for j in range(9):num = board[i][j]if num == '.':continue# 行判断if num in row[i]:print 'row=', numreturn Falseelse:row[i][num] = 1# 列判断if num in col[j]:print 'col=', numreturn Falseelse:col[j][num] = 1# 小正方形的区域判断area_index = area_index_dict[i//3][j//3]if num in area[area_index]:print 'area_index=', area_indexprint 'area=', numreturn Falseelse:area[area_index][num] = 1return True

我们可以看到,虽然题解中用到了如下循环:

for i in range(9):for j in range(9):# coding

但由于复杂度始终是 O(9×9)O(9×9),加上使用哈希表来判断元素是否存在,所以算法的复杂度始为 O(1)O(1)。

对数时间

若 T(n)=O(logn)T(n)=O(logn),则称其具有对数时间。

常见例子:

  • 二叉树相关操作
  • 二分查找

为什么是 logn?

什么是对数?

首先,我们复习一下 对数。

对数 是幂运算的逆运算。假如 x=βyx=βy,那么就有 y=logβxy=logβx。其中:

  • ββ 是对数的底(基底)
  • yy 就是 xx(对于底数 ββ)的对数

那我们说一个算法的复杂度是 O(logn)O(logn),那么 lognlogn 这个对数的底数去哪了?

换底公式

来看一下 换底公式:

logab=logcblogcalogab=logcblogca

假设两个算法复杂度分别为 O(logan)O(logan) 和 O(logbn)O(logbn),基于 换底公式 可以得到:

logan=logcnlogcalogan=logcnlogca

logbn=logcnlogcblogbn=logcnlogcb

对于 O(logan)O(logan) 和 O(logbn)O(logbn) 来说,只有一个常数因子的不同。在大 O 记法中我们丢弃该因子(忽略常数),因此无论对数的底是多少,我们将对数时间都记作 O(logn)O(logn)。

二分查找

对数时间最典型的算法应该就是二分查找了。例如这道题 搜索插入位置。

二分查找的基本思想:将查找的键和子数组的中间键作比较:

  • 如果被查找的键小于中间键,就在左子数组继续查找
  • 如果大于中间键,就在右子数组中查找
  • 否则中间键就是要找的元素

因此,对于 n 个元素的情况:

  • 第 1 次二分剩下元素 n2n2
  • 第 2 次二分剩下元素 n4n4
  • ……
  • 第 m 次二分剩下元素:n2mn2m

在最坏情况下,是在排除到只剩下最后一个值之后得到结果,即:

n2m=1n2m=1

由此可得:

2m=n2m=n

进而求出复杂度为 log2(n)log2(n)。又因为我们在大 O 记法中忽略底数 2,因此复杂度就是 O(logn)O(logn) 啦~

线性时间

如果一个算法的时间复杂度为 O(n)O(n),则称这个算法具有线性时间。随着样本数量的增加,复杂度也随之线性增加。常表现为单层循环。

来看一到例题 求众数。这里我们用了摩尔投票法,时间复杂度为 O(n)O(n)。

class Solution(object):def majorityElement(self, nums):""":type nums: List[int]:rtype: int"""major = 0count = 0for n in nums:if count == 0:major = nif n == major:count = count + 1else:count = count - 1return major

线性对数(准线性)时间

若算法复杂度为 T(n)=O(nlogn)T(n)=O(nlogn),则称这个算法具有线性对数时间。可以理解为执行了 n 次对数时间复杂度的操作。

有几种排序算法的平均时间复杂度都是线性对数时间,例如:

  • 堆排序:前 K 个高频元素
  • 快速排序:颜色分类
  • 归并排序

二次时间

若算法复杂度为 T(n)=O(n2)T(n)=O(n2),则称这个算法具有二次时间,即时间复杂度随着样本数量的增加呈平方数增长。常表现为双层循环。

常见的算法中有一写比较慢的排序算法,例如:

  • 冒泡排序
  • 选择排序
  • 插入排序

由于涉及的排序算法很多,若一一讲解的话就偏离这篇文章的侧重点了。如果大家对各类算法感兴趣可以参考:维基百科:排序算法。

算法是生活中的大智慧,而我们都是智慧的受益者。

a*算法的时间复杂度_从经典算法题看时间复杂度相关推荐

  1. 《算法之道》精华 经典算法部分

    <算法之道>精华 经典算法部分 本书作者邹恒明,作者另有一本书<数据结构之弦>,以及<操作系统之哲学原理>都是非常好的书 这本书能够算得上是深入浅出,文笔非常好.作 ...

  2. 冒泡和快速排序的时间复杂度_八大排序算法性能分析及总结

    一.排序算法说明 排序的定义:对一个无序的序列进行排序的过程. 输入:n个数:a1,a2,a3,-,an. 输出:n个数的排列:a1,a2,a3,-,an,使得a1<=a2<=a3< ...

  3. pythonsort函数时间复杂度_合并排序算法——时间复杂度详解和python代码实现

    递归形式 递归形式是算法中常用到的一种构造思路.递归允许函数或过程对自身进行调用,是对算法中重复过程的高度概括,从本质层面进行刻画,避免算法书写中过多的嵌套循环和分支语法.因此,是对算法结构很大的简化 ...

  4. python二分查找时间复杂度_二分查找算法的时间复杂度计算(logN)

    二分查找算法的时间复杂度计算(logN) 马富天 2019-08-10 20:25:24 54 [摘要]二分查找算法是对顺序查找算法的优化,二分查找算法的前提是数列是一个有序数列,递增或者递减,本文就 ...

  5. a*算法的时间复杂度_数据结构与算法系列——时间、空间复杂度

    数据结构和算法本质就是帮我们用最快的时间和最少的空间来执行我们的代码.所以,执行效率是衡量一个算法的非常重要的指标.那如何来计算你的算法代码的执行效率呢?这就需要时间.空间复杂度来分析了. 有人可能会 ...

  6. 一层循环时间复杂度_数据结构与算法系列——时间、空间复杂度

    数据结构和算法本质就是帮我们用最快的时间和最少的空间来执行我们的代码.所以,执行效率是衡量一个算法的非常重要的指标.那如何来计算你的算法代码的执行效率呢?这就需要时间.空间复杂度来分析了. 有人可能会 ...

  7. 冒泡和快速排序的时间复杂度_各种排序算法总结

    各种排序算法的稳定性,时间复杂度和空间复杂度总结: 我们比较时间复杂度函数的情况: 时间复杂度函数O(n)的增长情况: 所以对于n较大的排序记,一般的选择都是时间复杂度为O(nlog2n)的排序方法. ...

  8. 冒泡和快速排序的时间复杂度_常用排序算法之冒泡排序

    周末无事,带娃之余看到娃娃在算数,想到了排序-尝试着把几种常用的排序算法跟大家聊一聊,在分析的后面我会用GoLang.PHP和JS三种语言来实现下. 常见的基于选择的排序算法有冒泡排序.插入排序.选择 ...

  9. 数据结构排序算法实验报告_[数据结构与算法系列]排序算法(二)

    我的上一篇文章向大家介绍了排序算法中的冒泡排序.插入排序和选择排序.它们都是平均时间复杂度为 O(n^2) 的排序算法,同时还为大家讲解了什么是原地排序和什么是排序的稳定性.下图是这三种算法的比较,不 ...

最新文章

  1. Java中的I/O模型总结(八股文自述)
  2. Java NIO系列教程(九) ServerSocketChannel
  3. 学生计算机基础知识,初中计算机基础知识练习题集锦(学生)
  4. js一个按钮弹出两个按钮_没有电位器,用两个按钮可以实现变频器调速吗?
  5. r语言 xmlto html,R语言XML文件
  6. 基于Flink+ClickHouse构建实时游戏数据分析最佳实践
  7. SAP云平台测试帐号如何进入Neo环境
  8. Javadoc的Html文件传输chm
  9. MFC GDI+ 绘图
  10. ASP.NET AJAX Programmer's Reference : with ASP.NET 2.0 or ASP.NET 3.5
  11. vue 释放内存_13 道由浅入深的 Vue 自测题
  12. AES-128\192\256加密算法及其安全脆弱分析
  13. 使用Rufus制作安装U盘报错,使用UltraISO成功
  14. java的中文源代码
  15. poj2373 Dividing the Path (单调队列+dp)
  16. 特征金字塔:FPN网络 - Pytorch实现
  17. Shader学习12——简易图片叠加
  18. WiFi5 (802.11ac) Vs WiFi6(802.11ax)频宽及速率对比
  19. 忘记密码(找回密码)代码实现
  20. 你承认电子计算机是天之骄子改为双重否,双重否定句练习题

热门文章

  1. java生成sql语句_java生成SQL语句
  2. hppRNA-基于Snakemake的便捷无参数管道,可用于众多样品的RNA-Seq分析
  3. Java学习笔记(必看经典)
  4. JAVA基础16-Java匿名内部类
  5. php400错误的请求,Wordpress中的Ajax返回400错误请求,但不确定如何进一步调试 - WordPress - srcmini...
  6. Redis的优势和特点
  7. Tomcat(三):tomcat处理连接的详细过程
  8. rdp连接工具_如何在Windows10中清除RDP连接历史记录?
  9. 性能测试工具Lmbench的使用和下载
  10. 计算机常见知识获取方法,计算机信息处理技术基础知识