什么是算法?

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

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

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

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

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. 第一章 在.net mvc生成EF入门

    一. 打开Visual Studio 2017(我使用的是2017) 新建一个mvc项目 命名为StudentEntity 二.1)建立完项目后在项目中右击选择新建项,找到ADO.NET实体数据模型 ...

  2. C# Winform下一个热插拔的MIS/MRP/ERP框架(简介)

    Programmer普弱哥们都喜欢玩自己的框架,我也不例外. 理想中,这个框架要易于理解.易于扩展.易于维护:最重要的,易于CODING. 系统是1主体框架+N模组的多个EXE/DLL组成的,在主体框 ...

  3. 关于 kali linux

    2.更新系统:首先更换一个速度快点的国内源(1) lsb_release -a先看你的版本,是Rolling还是其他什么(2) leafpad /etc/apt/sources.list(源的默认文件 ...

  4. Java并发编程总结3——AQS、ReentrantLock、ReentrantReadWriteLock

    本文内容主要总结自<Java并发编程的艺术>第5章——Java中的锁. 一.AQS AbstractQueuedSynchronizer(简称AQS),队列同步器,是用来构建锁或者其他同步 ...

  5. css居中小结

    从css入门就开始接触,无所不在的,一直备受争议的居中问题. css居中分为水平居中和垂直居中,水平居中方式也较为常见和统一,垂直居中的方法就千奇百怪了. 博客原文地址:Claiyre的个人博客 ht ...

  6. JavaScript 的 Async\/Await 完胜 Promise 的六

    参考:http://www.10tiao.com/html/558/201705/2650964601/1.html Node 现在从版本 7.6 开始就支持 async/await 了. 简介: A ...

  7. maven封装jar包遇到的问题

    使用eclipse编译后可以生成jar包,使用mvn clean package指令打包报错,错误如下:No compiler is provided in this environment. Per ...

  8. POJ 2610

    #include<iostream> #include<iomanip> using namespace std; int main() { //freopen("a ...

  9. POJ 2538

    #include<string> //#include #include<stdio.h> #include<iostream> using namespace s ...

  10. android stdio 编译项目报Error:Failed to find target with hash string 'android-24

    android stdio 编译项目报Error:Failed to find target with hash string 'android-24 查看已有的SDK 设置项目的sdk为 25 an ...