什么是算法?

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

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

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

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

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. 在ASP.NET Core中,静态类如何读取配置文件

    这是今天下午一个同事问我的问题,如何在静态类中读取json配置文件.我当时并没有告诉他如何如何去做,办法肯定是有,但是这种编程思维确实得改改了.静态类.静态方法不是面向对象编程的最佳实践..NET C ...

  2. 【javascript】您好, 您要的ECMAScript6速记套餐到了 (一)

    [前言]本文“严重参考” 自阮一峰老师写的ES6文档,在此我郑重感谢他沉默无声的帮助 总结一下ES6为 javascript中的 对象/数组/函数 这JS三巨头所提供的更简洁优雅的书写方式,以及扩展的 ...

  3. HTML元素ID和JS方法名重复,JS调用失败

    HTML元素ID和JS方法名重复时,JS中的重名方法无法被找到,不能执行. 修改ID或者方法名,两者不一致即可.

  4. 深入理解java虚拟机读书笔记--java内存区域和管理

    第二章:Java内存区域和内存溢出异常 2.2运行时数据区域 运行时数据区分为方法区,堆,虚拟机栈,本地方法栈,程序计数器 方法区和堆是线程共享的区域 虚拟机栈,本地方法栈,程序计数器是数据隔离的数据 ...

  5. 10-01 Java 类,抽象类,接口的综合小练习--运动员和教练

    运动员和教练的案例分析 运动运和教练的案例 代码实现 /* 教练和运动员案例 乒乓球运动员和篮球运动员. 乒乓球教练和篮球教练. 为了出国交流,跟乒乓球相关的人员都需要学习英语. 请用所学知识: 分析 ...

  6. Java语言基础(方法与数组)_DAY05

    1:函数(掌握)   (1)定义在类中,有特定功能的一段小程序,可以独立运行.    (2)函数的格式:       修饰符 返回值类型 函数名(形参类型 形式参数1,形参类型 形式参数2...)   ...

  7. c3p0配置文件

    配置文件 名称必须为c3p0-config.xml,否则找不到: 标签名称 <c3p0-config> <default-config > 具体配置内容 </defaul ...

  8. flex布局中transform出错

    在flex布局下,若应用transform 的动画的子元素没有使用进行定位,则动画过程中,子元素将相对display:flex的元素进行static定位 动画结束后位置正常: 修复代码只需要posit ...

  9. Chrome 的 Material Design Refresh UI初探

    今天Chrome自动升级到69.0.3497.92, 发现UI已经变成了"Material Design Refresh". Chrome 浏览器的页面标签已经不再像以往那样倾斜和 ...

  10. iOS事件分发

    前段时间项目有一个需求,要在点击闪屏的时候做一些处理,刚接到这个需求觉得很简单啊,在原有的view上加个button或者手势识别啥的,后面实现的时候发现还是有点坑.无论我在闪屏上面加button还是手 ...