什么是算法?

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

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

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

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

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. Android常用库和插件

    下拉刷新 PullLoadMoreRecyclerView 实现RecyclerView下拉刷新和上拉加载更多以及RecyclerView线性.网格.瀑布流效果演示 https://github.co ...

  2. AJPFX平台:外汇的基本面分析

    AJPFX平台:开设外汇保证金交易账户以及入金之后,通常就可以开始交易了,但是在选择买卖时点时通常会依据两种分析,两种主要分析方法通常会被称为基本面分析和技术分析.基本面分析注重金融,经济理论和政局发 ...

  3. 30_网络编程-socketserver

    一.socketserver       socketserver可以实现和多个客户端通信.它是在socket的基础上进行了一层封装,也就是说底层还是调用的socket,在py2.7里面叫做Socke ...

  4. 【翻译】 Windows 内核漏洞学习—空指针解引用

    Windows Kernel Exploitation – NullPointer Dereference 原文地址:https://osandamalith.com/2017/06/22/windo ...

  5. fetch更新本地仓库两种方式:

    来源:https://www.cnblogs.com/chenlogin/p/6592228.html //方法一 $ git fetch origin master //从远程的origin仓库的m ...

  6. 网页关闭(解决window.close在火狐下不兼容问题)

    熟悉前端的都知道,火狐默认状态非window.open的页面window.close是无效的 网上有很多人说,在火狐的地址栏输入:about:config然后找到dom.allow_scripts_t ...

  7. python学习笔记14-函数

    使用关键字def来创建函数  注意缩进 函数命名规则: 1.必须以下划线或者字母开头 2.区分大小写 3.不能是保留字 调用函数一定记得加括号 def print_info(name,age) pri ...

  8. 设置iptables NAT出外网

    有时候云上部署环境,不能动态自设路由,没有公网ip地址的服务器,只能通过NAT的方式出外网,下面就记录一下设置过程. 当前状态 服务器A只有一个内网IP,不能上外网,内网IP与服务器B内网相通:服务器 ...

  9. [工具]Cobalt Strike 3.13 TeamServer for Windows

    Cobalt Strike 3.13 TeamServer for Windows 0x001 环境 CS 3.12 或 3.13 Kali或Win最好安装jdk1.8.5或之后版本 设置环境变量,如 ...

  10. 【disruptor】1、关于disruptor中的SequenceBarrier对象

    首先这个类的uml结构在disruptor中是这样的,里面只有部分的属性对象和函数内容,具体有什么作用,用到了再说,用不到我也不会... 1.那么这个对象有什么用呢? 注意我们这个类中有哪些属性: 我 ...