Java教程

算法的时间与空间复杂度

本文主要是介绍算法的时间与空间复杂度,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

        年底了,小编准备去面试一波,去看看现在市面上对我这种水平的要求是什么。然后面试了许多的公司,经历了许多主程的吐槽和致命提问。接下来的时间直到到年底左右,都会将面试的问题以及答案一一写下来,展示给各位读者。

那么接下里就开始介绍第一个问题。

直接插入法的复杂度是多少?

当时小编并不知道,所以懵了一下,回答的是0(n^2)和O(1)。然后面试官问我为什么是O(1),然后我直接说蒙的。然后面试官瞬间沉默了,他当时可能在想这个人到底是个什么妖魔鬼怪,这都能靠运气。好吧,说实话,其实是之前复习过,但是因为忘记了,所以顺口猜了一个。好了,言归正传,这时候,其实这个问题要分成三个问题来考虑。

  1. 复杂度是指空间复杂度还是时间复杂度。
  2. 空间复杂度和时间复杂度怎么计算。
  3. 直接插入法是什么东东。

然后让我们来逐个分析。


        算法(Algorithm)是指用来操作数据、解决程序问题的一组方法。对于同一个问题,使用不同的算法,也许结果一样,但是过程中消耗的资源和时间却不同。那么就需要有一个东西或者有一个衡量时间复杂度的标志,那就是复杂度。然后复杂度又可以从「时间」和「空间」两个维度去考量。

  • 时间维度:是指执行当前算法所消耗的时间,我们通常用「时间复杂度」来描述。
  • 空间维度:是指执行当前算法需要占用多少内存空间,我们通常用「空间复杂度」来描述。

下面我来分别介绍一下「时间复杂度」和「空间复杂度」的计算方式。

一、时间复杂度

很多人首先想到的方法是把这个算法程序运行一遍,那么她的消耗就自然而然知道了。这个方法可以,但是又很多的弊端。

  1. 这种方法受环境的影响,受硬件性能的影响
  2. 我们在写算法的时候,并没有办法完整的去运行

因此,另一种更为通用的方法就出来了:「 大O符号表示法 」,即 T(n) = O(f(n))

在 大O符号表示法中,时间复杂度的公式是: T(n) = O( f(n) ),其中f(n) 表示每行代码执行次数之和,而 O 表示正比例关系,这个公式的全称是:算法的渐进时间复杂度

先上例子:

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

假设每行代码的执行时间都是一样的,我们用1秒来表示。那么第一行耗时是1秒,第三行执行时间是n秒,第四行执行时间也是n秒(第2行和第5行是符号,先忽略),那么总时间是1秒+n秒+n秒,即:(1+2n)秒时间。即:T(n) = (1+2n)的时间。从这个结果可以看出,这个算法的耗时是随着n的变化而变化。又因为因为大O符号表示法并不是用于来真实代表算法的执行时间的,它是用来表示代码执行时间的增长变化趋势的。 所以上面的例子中,如果n无限大的时候,T(n) = time(1+2n)中的常量1就没有意义了,倍数2也意义不大。因此直接简化为T(n) = O(n) 就可以了。因此,我们可以简化的将这个算法的时间复杂度表示为:T(n) = O(n)

接下来介绍一下常见的时间复杂度量级:

1、常数阶O(1)

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

无论代码执行了多少行,只要是没有循环等复杂结构,那这个代码的时间复杂度就都是O(1),它消耗的时候并不随着某个变量的增长而增长,那么无论这类代码有多长,即使有几万几十万行,都可以用O(1)来表示它的时间复杂度。

2、线性阶O(n)

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

for循环里面的代码会执行n遍,因此它消耗的时间是随着n的变化而变化的,因此这类代码都可以用O(n)来表示它的时间复杂度。

3、对数阶O(logN)

先看代码:

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

从上面代码可以看到,在while循环里面,每次都将 i 乘以 2,乘完之后,i 距离 n 就越来越近了。我们试着求解一下,假设循环x次之后,i 就大于 2 了,此时这个循环就退出了,也就是说 2 的 x 次方等于 n,那么 x = log2^n
也就是说当循环 log2^n 次以后,这个代码就结束了。因此这个代码的时间复杂度为:O(logn)

4、线性对数阶O(n*logN)

线性对数阶O(nlogN) 其实非常容易理解,将时间复杂度为O(logn)的代码循环N遍的话,那么它的时间复杂度就是 n * O(logN),也就是了O(nlogN)。如:

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

5、平方阶O(n²)

平方阶O(n²) 就更容易理解了,如果把 O(n) 的代码再嵌套循环一遍,它的时间复杂度就是 O(n²) 了。如:

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

这段代码其实就是嵌套了2层n循环,它的时间复杂度就是 O(n*n),即 O(n²)
如果将其中一层循环的n改成m,即:

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

那它的时间复杂度就变成了 O(m*n)

5、立方阶O(n³)K次方阶O(n^k)

参考上面的O(n²) 去理解就好了,O(n³)相当于三层n循环,其它的类似。

除此之外,其实还有 平均时间复杂度、均摊时间复杂度、最坏时间复杂度、最好时间复杂度 的分析方法,有点复杂,这里就不展开了。

二、空间复杂度

空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一个量度,同样反映的是一个趋势,我们用 S(n) 来定义。

空间复杂度比较常用的有:O(1)、O(n)、O(n²),我们下面来看看:

1、空间复杂度O(1)

如果算法执行所需要的临时空间不随着某个变量n的大小而变化,即此算法空间复杂度为一个常量,可表示为 O(1)。如:

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

代码中的 i、j、m 所分配的空间都不随着处理数据量变化,因此它的空间复杂度 S(n) = O(1)

2、空间复杂度O(n)

我们先看一个代码:

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

这段代码中,第一行new了一个数组出来,这个数据占用的大小为n,这段代码的2-6行,虽然有循环,但没有再分配新的空间,因此,这段代码的空间复杂度主要看第一行即可,即 S(n) = O(n)zhe


总结:

这篇文章可以说是小编自己学习的记录,原文是参考知乎某个大佬的文章。

点击查看原文

可以说整篇文章都是拷贝过来的,这个大佬的文章一看就懂,明天接着介绍《直接插入算法》,然后分析直接插入算法的的复杂度。敬请期待!!!

这篇关于算法的时间与空间复杂度的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!