什么是算法?

计算机是人的大脑的延伸,它的存在主要是为了帮助我们解决问题。

而算法在计算机领域中就是为了解决问题而指定的一系列简单的指令集合。不同的算法需要不同的资源,例如:执行时间或消耗内存。

如果一个算法执行时间需要好几年或者需要占用非常大的内存,那么这算法几乎毫无用处,即使有价值使用场景也非常有限。

因此,一般上我们讨论一个算法的优劣的时候可以通过时间和空间两个维度来衡量,也就是常说的:

1、时间复杂度;

2、空间复杂度;

我们当然希望执行时间和消耗内存都越少越好,但很多时候其实我们无法同时兼顾,需要在时间和空间之间做一定的取舍达到平衡。

时间复杂度

一般上,如果我们要衡量一个程序片段的执行时间,我们会把程序运行一次并打印时间,这是最常见也是最简单的方式。

这种方式存在一些问题:

1、不同的计算机会产生不同的执行时间,甚至于相同的计算机也会产生不同的时间,根据计算机当前的情况而定;

2、通常可能我们使用很小的数据量来测量,但一个算法随着数据量的不同性能变化是不同的,所以小数据量衡量的时间不见得适用于大数据量;

3、甚至于有时候一个算法压根无法直接通过运行来测试时间。

为了解决这些问题,引入了数学领域中的 “大O标记法”

大O标记法

数学概念:如果存在正常数c和n,使得当N≥n的时候T(N)≤cf(N),则标记为T(N)=O(f(N))

数学概念看起来有些费解,我们可以把T(N)=1000N把f(N)=N2,当N=1000,c=1的时候,1000N=N2。而当N>1000的时候,N2>1000N。

也就是说,当N无限大的时候,N2的值将必定大于1000N的值,也就是说1000N这个函数的值不会超过N2,或者说N2是1000N的上界,它限定了1000N的最大值。

如果我们考虑一个算法最糟糕的时候会执行多久,则大O标记法很轻易就能表示出来。

下面我来看一个例子,关于大O标记法怎么衡量算法时间:

 int total = 0;
for (int i = 0; i < n ; i++ ) {
total += i;
}

我们计算一下代码的执行时间:

1、第1行有一个赋值操作记1个单位时间;

2、第2行有一个赋值操作记1个单位时间,一个比较操作记n+1个单位时间,自增运算记n个单位时间,合计:2n+2个时间;

3、第3行在循环体中执行n次,我们记为2*n个单位时间;

以上代码合计时间为:4n+3,也就是 T(n) = O(4n+3),O(4n+3)表示程序的运行时间上界。如果n无限大的时候,我们忽略倍数4和常数3,则T(n)的运行时间为O(n);

我们也可以说,以上代码的时间复杂度为O(n)。

上面的例子中,我们分析了每一行的代码运行时间,并最终得出时间复杂度为O(n),但一个算法的复杂度有时候让我们难以像上面这样每一步都去计算并合计时间,由此我们可以得出一些简单的原则来计算时间

1、常数阶O(1)

无论代码执行多少行,只要代码中没有for等循环结构,那么复杂度就是O(1),如:

 int i = 1;
int j = 2;
++i;
j++;
int m = i + j;

无论程序中数据量有多少,都不影响代码的运行时间,则为常数阶。

2、线性阶O(N)

如果存在一个循环体,那么循环n次,则复杂度为O(N),如:

 for(i=1; i<=n; ++i)
{
j = i;
j++;
}

3、对数阶O(logN)

线性阶O(N)的情况是循环了N次,所以对数阶的情况就是循环了logN次,如:

 int i = 1;
while(i<n)
{
i = i * 2;
}

我们假设while循环体在循环了x次之后退出循环,那么也就是i*2x≥n,时间复杂度的上界也就是log2n=x,我们标记为O(logN)。

4、线性对数O(NlogN)

对数阶是logN,那么nlogN即使把对数阶循环n次,如:

 for(m=1; m<n; m++)
{
i = 1;
while(i<n)
{
i = i * 2;
}
}

5、平方阶O(N2)

平方阶很容易理解,嵌套循环即是,如:

 for(x=1; i<=n; x++)
{
for(i=1; i<=n; i++)
{
j = i;
j++;
}
}

根据平方阶可以推出,立方阶O(N3),k次方阶O(Nk),或者O(n * m)

空间复杂度

时间复杂度粗略估计了执行时间的上界,空间复杂度也是类似的,我们看几个常见的示例:

1、空间复杂度O(1)

和时间复杂度一样,一个随着数据量的变化内存消耗不变化的时候,我们认为空间复杂度为常数也就是O(1),如:

 int i = 1;
int j = 2;
++i;
j++;
int m = i + j;

2、空间复杂度O(N)

随着数据量变化,内存消耗呈线性变化的时候,我们称之为O(N),如:

 int[] m = new int[n]
for(i=1; i<=n; ++i)
{
j = i;
j++;
}

这里的数组m随着数据量n的变化线性增长。

3、空间复杂度O(N2)

随着数据量的变化,内存消耗为平方变化,如:

 int[][] arr = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
arr[i][j] = new Random().nextInt();
}
}

以上二维数组中,当n+1的时候,arr数组大小从n*n变为了(n+1)*(n+1),其它空间复杂度以此类推

总结

其实无论是时间复杂度还是空间复杂度,都是在考虑当n变化的时候时间或者空间会呈什么样的变化,并最终确定时间和空间的上界问题。

考虑时间复杂度的时候,我们简化为思考n+1的时候,循环次数如何变化,如果不变则O(1),线性则O(N),对数则log(N)...以此类推。也就是说把n当作问题规模,当n变化的时候,执行次数的变化呈现什么规律。

考虑空间复杂度的时候,我们简化为思考n+1的时候,内存消耗的数量如何变化,如果不变则为O(1),线性则为O(N),平方则为O(N2)...以此类推。也就是说当n变化的时候,内存消耗的变化呈现什么规律。

本文讨论的是在n不断增长到无限大最糟糕的情况下时间与空间复杂度的问题,但我们程序中不是每个算法都需要考虑n无限大问题,也就是说如果我们的n是有限的且是很小的值我们甚至完全可以不考虑它的执行时间或者空间问题。

参考文章:https://blog.csdn.net/jsjwk/article/details/84315770

算法时间复杂度、空间复杂度(大O表示法)的更多相关文章

  1. 1. 时间复杂度(大O表示法)以及使用python实现栈

    1.时间复杂度(大O表示法): O(1) < O(logn) < O(n) < O(nlogn) < O(n2) < O(n3) < O(2n) < O(n! ...

  2. python数据结构与算法学习自修第二天【时间复杂度与大O表示法】

    #!/usr/bin/env python #! _*_ coding:UTF-8 _*_ from Queue import Queue import time que = Queue() time ...

  3. 白话算法:时间复杂度和大O表示法

    转自:https://www.jianshu.com/p/59d09b9cee58 每一个优秀的开发者脑中都有时间概念.他们想给用户更多的时间让用户做他们想做的事情.他们通过最小化时间复杂度来实现这一 ...

  4. 时间复杂度和大O表示法

    大O表示法:称一个函数g(n)是O(f(n)),当且仅当存在常数c>0和n0>=1,对一切n>n0均有|g(n)|<=c|f(n)|成立,也称函数g(n)以f(n)为界或者称g ...

  5. 算法图解之大O表示法

    什么是大O表示法 大O表示法可以告诉我们算法的快慢. 大O比较的是操作数,它指出了算法运行时间的增速. O(n) 括号里的是操作数. 举例 画一个16个格子的网格,下面分别列举几种不同的画法,并用大O ...

  6. 常见算法的时间复杂度(大O计数法)

    定义 ​ 对于不同的机器环境而言,确切的单位时间是不同的,但是对于算法进行多少个基本操作(即花费多少时间单位)在规模数量级上却是相同的,由此可以忽略机器环境的影响而客观的反应算法的时间效率. 对于算法 ...

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

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

  8. 重拾算法之复杂度分析(大O表示法)

    .katex { display: block; text-align: center; white-space: nowrap; } .katex-display > .katex > ...

  9. 算法的时间复杂度——"大O分析法"(转载)

    原文地址:https://my.oschina.net/gooke/blog/684026 一下为本人笔记:) 场景:在解决计算机科学领域的问题时,经常有好多个方法都可以,想找到最优的方法,就有了时间 ...

随机推荐

  1. Sql Server中的表访问方式Table Scan, Index Scan, Index Seek

    1.oracle中的表访问方式 在oracle中有表访问方式的说法,访问表中的数据主要通过三种方式进行访问: 全表扫描(full table scan),直接访问数据页,查找满足条件的数据 通过row ...

  2. CEF3编译添加mp4支持(对应CefSharp63.0.3),chromium63(3239),附release下载

    编译环境需求(3239版本) win7或更高,64位 vs2017 15.3.2+ 默认位置安装 不需要安装附带的win10sdk,sdk单独装 Windows 10.0.15063.468 SDK ...

  3. 在EF中使用Expression自动生成p=>new Entity(){X="",Y="",..}格式的Lambda表达式灵活实现按需更新

    一.基本介绍      回忆:最早接触Expression是在学校接触到EF的时候,发现where方法里的参数是Expression<Func<T,bool>>这么一个类型,当 ...

  4. C#6.0语言规范(十四) 枚举

    一个枚举类型是一个独特的值类型(值类型)声明一组命名的常量. 这个例子 enum Color { Red, Green, Blue } 声明了一个名为枚举类型Color与成员Red,Green和Blu ...

  5. Network - Tips

    001 - 查询whois 可通过在线工具进行查询: https://www.whois365.com http://whois.aliyun.com http://whois.chinaz.com ...

  6. day 73 初学vue (1)

    前情提要: vue 框架的初学习, 主要是,指令,属性,函数,计算属性,监听属性,钩子,生命周期,过滤器,阻止事件和综合案例todo list 学习准备,感谢学习资源: vue 官网:https:// ...

  7. postgresql-日志表

    pg_log,数据库日志表postgresqllog CREATE TABLE postgres_log ( log_time timestamp(3) with time zone, 日志生成时间 ...

  8. vue插件ele使用小坑

    1.ele-table组件中selection如何默认选中 使用官网提供的api-->>Table Methods中的toggleRowSelection,关于这个api基本介绍就不说了. ...

  9. Android之内存泄漏

    开篇之前,我们要先理解:什么是内存泄漏.百度百科:内存泄漏(Memory Leak)是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等 ...

  10. Git学习系列之Windows上安装Git之后的一些配置(图文详解)

    不多说,直接上干货! 前面博客 Git学习系列之Windows上安装Git详细步骤(图文详解) 第一次使用Git时,需要对Git进行一些配置,以方便使用Git. 不过,这种配置工作只需要进行一次便可, ...