时间复杂度&空间复杂度

时间复杂度&空间复杂度

时间复杂度是算法分析中常用的方法。他给出了数学方法来分析算法的执行效率。本文讨论如何计算时间复杂度,并给出相应的实例。而空间复杂度则是一个程序所消耗的内存成本。这两个直接决定一个程序的运行效率以及一个算法是否高效。

时间复杂度

定义

时间频度

时间频度 一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多,哪个算法花费的时间少就可以了。并且一个算法花费的时间与算法中语句的执行次数成正比例,哪个算法中语句执行次数多,它花费时间就多。一个算法中的语句执行次数称为语句频度或时间频度。记为T(n)。

时间复杂度

如果一个问题的规模是n,解这一问题的某一算法所需要的时间为T(n),它是n的某一函数 T(n)称为这一算法的“时间复杂性”。

当输入量n逐渐加大时,时间复杂性的极限情形称为算法的“渐近时间复杂性”。

时间频度不同,但时间复杂度可能相同。如:T(n)=n2+3n+4与T(n)=4n2+2n+1它们的频度不同,但时间复杂度相同,都为O(n2)。

按数量级递增排列,常见的时间复杂度有:常数阶O(1),对数阶O(log2n),线性阶O(n), 线性对数阶O(nlog2n),平方阶O(n2),立方阶O(n3),…, k次方阶O(nk),指数阶O(2n)。随着问题规模n的不断增大,上述时间复杂度不断增大,算法的执行效率越低。

大O记法

我们常用大O表示法表示时间复杂性,注意它是某一个算法的时间复杂性。大O表示只是说有上界,由定义如果f(n)=O(n),那显然成立f(n)=O(n^2),它给你一个上界,但并不是上确界,但人们在表示的时候一般都习惯表示前者。

此外,一个问题本身也有它的复杂性,如果某个算法的复杂性到达了这个问题复杂性的下界,那就称这样的算法是最佳算法

“大O记法”:在这种描述中使用的基本参数是 n,即问题实例的规模,把复杂性或运行时间表达为n的函数。这里的“O”表示量级 (order),比如说“二分检索是 O(logn)的”,也就是说它需要“通过logn量级的步骤去检索一个规模为n的数组”记法 O ( f(n) )表示当 n增大时,运行时间至多将以正比于 f(n)的速度增长。

这种渐进估计对算法的理论分析和大致比较是非常有价值的,但在实践中细节也可能造成差异。例如,一个低附加代价的O(n2)算法在n较小的情况下可能比一个高附加代价的 O(nlogn)算法运行得更快。当然,随着n足够大以后,具有较慢上升函数的算法必然工作得更快。

最坏时间和平均时间

最坏情况下的时间复杂度称最坏时间复杂度。一般不特别说明,讨论的时间复杂度均是最坏情况下的时间复杂度。 这样做的原因是:最坏情况下的时间复杂度是算法在任何输入实例上运行时间的上界,这就保证了算法的运行时间不会比任何更长。

在最坏情况下的时间复杂度为T(n)=0(n),它表示对于任何输入实例,该算法的运行时间不可能大于0(n)。 平均时间复杂度是指所有可能的输入实例均以等概率出现的情况下,算法的期望运行时间。

计算方法

求解算法的时间复杂度的具体步骤是:

  • ⑴ 找出算法中的基本语句;

      算法中执行次数最多的那条语句就是基本语句,通常是最内层循环的循环体。

  • ⑵ 计算基本语句的执行次数的数量级;

      只需计算基本语句执行次数的数量级,这就意味着只要保证基本语句执行次数的函数中的最高次幂正确即可,可以忽略所有低次幂和最高次幂的系数。这样能够简化算法分析,并且使注意力集中在最重要的一点上:增长率。

  • ⑶ 用大Ο记号表示算法的时间性能。

      将基本语句执行次数的数量级放入大Ο记号中。

具体实例

O(1)

常数阶,即时间复杂度不随着规模n的增大而增大。如果算法的执行时间不随着问题规模n的增加而增长,即使算法中有上千条语句,其执行时间也不过是一个较大的常数

int x=91; 
int y=100;
while(y>0) {
    if(x>100) {
        x=x-10;
        y--;
    } else {
    x++;
    }
}

T(n)=O(1),

这个程序看起来有点吓人,总共循环运行了1000次,但是我们看到n没有?没。这段程序的运行是和n无关的, 
就算它再循环一万年,我们也不管他,只是一个常数阶的函数。

O(n)

一次阶时间复杂度。耗费的时间与规模n成一阶线性关系。

int n;
cin >> n;
while (n--) {
    cout << n << endl;
}

O(n^3)

三阶线性关系。(n阶线性以此类推即可。)就好像矩阵相乘就永远是O(n^3 )

int total = 0;
int n;
cin >> n;
for (int i = 0; i != n; i++) {
    for (int j = 0; j != i; j++) {
        for (int k = 0; j != j; k++) {
            total++;
        }
    }
}

内循环的执行次数虽然与问题规模n没有直接关系,但是却与外层循环的变量取值有关,而最外层循环的次数直接与n有关,因此可以从内层循环向外层分析语句的执行次数:则该程序段的时间复杂度为T(n)=O(n^3 /6+低次项)=O(n^3)

常见的算法时间复杂度由小到大依次为:

  Ο(1)<Ο(log2(n)<Ο(n)<Ο(nlog2(n))<Ο(n^2) <Ο(n^3) <…<Ο(2^n) <Ο(n!)

分析法则

在计算算法时间复杂度时有以下几个简单的程序分析法则:

  • 1.对于一些简单的输入输出语句或赋值语句,近似认为需要O(1)时间

  • 2.对于顺序结构,需要依次执行一系列语句所用的时间可采用大O下”求和法则”

  • 3.求和法则:是指若算法的2个部分时间复杂度分别为 T1(n)=O(f(n))和 T2(n)=O(g(n)),则 T1(n)+T2(n)=O(max(f(n), g(n)))

    特别地,若T1(m)=O(f(m)), T2(n)=O(g(n)),则 T1(m)+T2(n)=O(f(m) + g(n)) 
    3.对于选择结构,如if语句,它的主要时间耗费是在执行then字句或else字句所用的时间,需注意的是检验条件也需要O(1)时间

  • 4.对于循环结构,循环语句的运行时间主要体现在多次迭代中执行循环体以及检验循环条件的时间耗费,一般可用大O下”乘法法则”

    乘法法则: 是指若算法的2个部分时间复杂度分别为 T1(n)=O(f(n))和 T2(n)=O(g(n)),则 T1*T2=O(f(n)*g(n))

  • 5.对于复杂的算法,可以将它分成几个容易估算的部分,然后利用求和法则和乘法法则技术整个算法的时间复杂度

另外还有以下2个运算法则:

(1) 若g(n)=O(f(n)),则O(f(n))+ O(g(n))= O(f(n))

(2) O(Cf(n)) = O(f(n)),其中C是一个正常数

空间复杂度

一个程序的空间复杂度是指运行完一个程序所需内存的大小。利用程序的空间复杂度,可以对程序的运行所需要的内存多少有个预先估计。一个程序执行时除了需要存储空间和存储本身所使用的指令、常数、变量和输入数据外,还需要一些对数据进行操作的工作单元和存储一些为现实计算所需信息的辅助空间。程序执行时所需存储空间包括以下两部分。  

(1)固定部分。这部分空间的大小与输入/输出的数据的个数多少、数值无关。主要包括指令空间(即代码空间)、数据空间(常量、简单变量)等所占的空间。这部分属于静态空间。

(2)可变空间,这部分空间的主要包括动态分配的空间,以及递归栈所需的空间等。这部分的空间大小与算法有关。

一个算法所需的存储空间用f(n)表示。S(n)=O(f(n)) 其中n为问题的规模,S(n)表示空间复杂度。

空间复杂度 
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。 
计算方法: 
①忽略常数,用O(1)表示 
②递归算法的空间复杂度=递归深度N*每次递归所要的辅助空间 
③对于单线程来说,递归有运行时堆栈,求的是递归最深的那一次压栈所耗费的空间的个数,因为递归最深的那一次所耗费的空间足以容纳它所有递归过程。

如:

<span style="color:#000000"><code><span style="color:#000088">int</span> a;
<span style="color:#000088">int</span> b;
<span style="color:#000088">int</span> c;
<span style="color:#000088">printf</span>(<span style="color:#009900">"<span style="color:#4f4f4f">%d</span> <span style="color:#4f4f4f">%d</span> <span style="color:#4f4f4f">%d</span> \n"</span>,a,b,c);</code></span>

它的空间复杂度O(n)=O(1);

<span style="color:#000000"><code><span style="color:#000088">int</span> fun(<span style="color:#000088">int</span> n,)
{
<span style="color:#000088">int</span> k=<span style="color:#006666">10</span>;
<span style="color:#000088">if</span>(n==k)
<span style="color:#000088">return</span> n;
<span style="color:#000088">else</span>
<span style="color:#000088">return</span> fun(++n);
}</code></span>

递归实现,调用fun函数,每次都创建1个变量k。调用n次,空间复杂度O(n*1)=O(n)。

举例说明

1:实现二分查找算法的递归及非递归。(分析时间复杂度及空间复杂度)

迭代算法

<span style="color:#000000"><code>#define _CRT_SECURE_NO_WARNINGS

#include<stdio.h>
#include<<span style="color:#4f4f4f">string</span>.h>
#include<assert.h>

<span style="color:#4f4f4f">int</span> BinarySearch(<span style="color:#4f4f4f">int</span> arr[], <span style="color:#4f4f4f">int</span> <span style="color:#4f4f4f">len</span>, <span style="color:#4f4f4f">int</span> num)
{
    assert(arr);

    <span style="color:#4f4f4f">int</span> <span style="color:#4f4f4f">left</span> = <span style="color:#006666">0</span>;
    <span style="color:#4f4f4f">int</span> <span style="color:#4f4f4f">right</span> = <span style="color:#4f4f4f">len</span> - <span style="color:#006666">1</span>;
    <span style="color:#4f4f4f">int</span> <span style="color:#4f4f4f">mid</span>;

    <span style="color:#000088">while</span> (<span style="color:#4f4f4f">left</span> <= <span style="color:#4f4f4f">right</span>)
    {
        <span style="color:#4f4f4f">mid</span> = <span style="color:#4f4f4f">left</span> + (<span style="color:#4f4f4f">right</span> - <span style="color:#4f4f4f">left</span>) / <span style="color:#006666">2</span>;

        <span style="color:#000088">if</span> (num > arr[<span style="color:#4f4f4f">mid</span>])
        {
            <span style="color:#4f4f4f">left</span> = <span style="color:#4f4f4f">mid</span> + <span style="color:#006666">1</span>;
        }
        <span style="color:#000088">else</span> <span style="color:#000088">if</span> (num < arr[<span style="color:#4f4f4f">mid</span>])
        {
            <span style="color:#4f4f4f">right</span> = <span style="color:#4f4f4f">mid</span> - <span style="color:#006666">1</span>;
        }
        <span style="color:#000088">else</span>
        {
            return <span style="color:#4f4f4f">mid</span>;
        }
    }

    return -<span style="color:#006666">1</span>;
}



<span style="color:#4f4f4f">int</span> main()
{
    <span style="color:#4f4f4f">int</span> arr[] = { <span style="color:#006666">1</span>,<span style="color:#006666">2</span>,<span style="color:#006666">3</span>,<span style="color:#006666">4</span>,<span style="color:#006666">5</span>,<span style="color:#006666">6</span>,<span style="color:#006666">7</span>,<span style="color:#006666">8</span>,<span style="color:#006666">9</span> };
    <span style="color:#4f4f4f">int</span> length = sizeof(arr) / sizeof(arr[<span style="color:#006666">0</span>]);
    <span style="color:#4f4f4f">int</span> aim = <span style="color:#006666">9</span>;
    <span style="color:#4f4f4f">int</span> result;

    result = BinarySearch(arr, length, aim);

    <span style="color:#000088">if</span> (result == -<span style="color:#006666">1</span>)
    {
        printf(<span style="color:#009900">"Can't find %d\n"</span>, aim);
    }
    <span style="color:#000088">else</span>
    {
        printf(<span style="color:#009900">"%d at %d postion\n"</span>, aim,result + <span style="color:#006666">1</span>);
    }


    return <span style="color:#006666">0</span>;
}</code></span>

二分查找时,每次都在原有查找内容进行二分,所以时间复杂度为O(log2 n) 
因为变量值创建一次,所以空间复杂度为O(1)

递归算法

<span style="color:#000000"><code><span style="color:#4f4f4f">int</span> BinarySearchRecursion(<span style="color:#4f4f4f">int</span> arr[<span style="color:#006666">5</span>], <span style="color:#4f4f4f">int</span> lef, <span style="color:#4f4f4f">int</span> rig,<span style="color:#4f4f4f">int</span> aim)
{
    <span style="color:#4f4f4f">int</span> <span style="color:#4f4f4f">mid</span> = lef + (rig - lef) / <span style="color:#006666">2</span>;


    <span style="color:#000088">if</span> (lef <= rig)
    {
        <span style="color:#000088">if</span> (aim < arr[<span style="color:#4f4f4f">mid</span>])
        {
            rig = <span style="color:#4f4f4f">mid</span> - <span style="color:#006666">1</span>;
            BinarySearchRecursion(arr, lef, rig, aim);
        }
        <span style="color:#000088">else</span> <span style="color:#000088">if</span> (arr[<span style="color:#4f4f4f">mid</span>] < aim)
        {
            lef = <span style="color:#4f4f4f">mid</span> + <span style="color:#006666">1</span>;
            BinarySearchRecursion(arr, lef, rig, aim);
        } 
        <span style="color:#000088">else</span> <span style="color:#000088">if</span> (aim == arr[<span style="color:#4f4f4f">mid</span>])
        {
            return <span style="color:#4f4f4f">mid</span>;
        }

    }
    <span style="color:#000088">else</span>
        return -<span style="color:#006666">1</span>;

}


<span style="color:#4f4f4f">int</span> main()
{
    <span style="color:#4f4f4f">int</span> arr[] = { <span style="color:#006666">1</span>,<span style="color:#006666">2</span>,<span style="color:#006666">3</span>,<span style="color:#006666">5</span>,<span style="color:#006666">6</span>, };
    <span style="color:#4f4f4f">int</span> sz = sizeof(arr)/sizeof(arr[<span style="color:#006666">0</span>]);
    <span style="color:#4f4f4f">int</span> result;

    result = BinarySearchRecursion(arr, <span style="color:#006666">0</span>, sz - <span style="color:#006666">1</span>, <span style="color:#006666">4</span>);

    <span style="color:#000088">if</span> (-<span style="color:#006666">1</span> == result)
    {
        printf(<span style="color:#009900">"Can't find it.\n"</span>);
    }
    <span style="color:#000088">else</span>
        printf(<span style="color:#009900">"Aim at %d location\n"</span>, result+<span style="color:#006666">1</span>);
}</code></span>

时间复杂度为O(log2 n) 
每进行一次递归都会创建变量,所以空间复杂度为O(log2 n)

2:实现斐波那契数列的递归及非递归。(分析时间复杂度及空间复杂度)

迭代算法

<span style="color:#000000"><code><span style="color:#000088">int</span> FeiBoNaCciInteration(<span style="color:#000088">int</span> a,<span style="color:#000088">int</span> b,<span style="color:#000088">int</span> num)
{
    <span style="color:#000088">int</span> c;

    <span style="color:#000088">if</span> (num <= <span style="color:#006666">0</span>)
        <span style="color:#000088">return</span> -<span style="color:#006666">1</span>;
    <span style="color:#000088">else</span> <span style="color:#000088">if</span> (num == <span style="color:#006666">1</span>)
        <span style="color:#000088">return</span> a;
    <span style="color:#000088">else</span> <span style="color:#000088">if</span> (num == <span style="color:#006666">2</span>)
        <span style="color:#000088">return</span> b;
    <span style="color:#000088">else</span>
    {
        <span style="color:#000088">while</span> (num - <span style="color:#006666">2</span>)
        {
            c = a + b;
            a = b;
            b = c;
            num--;
        }
        <span style="color:#000088">return</span> c;
    }

}

<span style="color:#000088">int</span> main()
{
    <span style="color:#000088">int</span> n;
    <span style="color:#000088">int</span> result;

    <span style="color:#000088">printf</span>(<span style="color:#009900">"Input n\n"</span>);
    scanf(<span style="color:#009900">"<span style="color:#4f4f4f">%d</span>"</span>, &n);

    result = FeiBoNaCciInteration(<span style="color:#006666">2</span>, <span style="color:#006666">3</span>, n);<span style="color:#008800">//</span>可自定义输入第一个数和第二个数
    <span style="color:#000088">if</span> (result == -<span style="color:#006666">1</span>)
    {
        <span style="color:#000088">printf</span>(<span style="color:#009900">"Input Error!\n"</span>);
    }
    <span style="color:#000088">else</span>
    {
        <span style="color:#000088">printf</span>(<span style="color:#009900">"n is <span style="color:#4f4f4f">%d</span>\n"</span>, result);
    }

    <span style="color:#000088">return</span> <span style="color:#006666">0</span>;
}</code></span>

时间复杂度O(n) 
空间复杂度为O(1)

递归算法

<span style="color:#000000"><code><span style="color:#000088">int</span> FeiBoNaCciRecursion(<span style="color:#000088">int</span> num)
{
    <span style="color:#000088">if</span> (num < <span style="color:#006666">0</span>)
        <span style="color:#000088">return</span> -<span style="color:#006666">1</span>;
    <span style="color:#000088">if</span> (num <= <span style="color:#006666">2</span> && num > <span style="color:#006666">0</span>)
        <span style="color:#000088">return</span> <span style="color:#006666">1</span>;
    <span style="color:#000088">else</span>
        <span style="color:#000088">return</span> FeiBoNaCciRecursion(num - <span style="color:#006666">1</span>) + FeiBoNaCciRecursion(num - <span style="color:#006666">2</span>);

}

<span style="color:#000088">int</span> main()
{
    <span style="color:#000088">int</span> n;
    <span style="color:#000088">int</span> result;

    <span style="color:#000088">printf</span>(<span style="color:#009900">"Input n\n"</span>);
    scanf(<span style="color:#009900">"<span style="color:#4f4f4f">%d</span>"</span>, &n);

    result = FeiBoNaCciRecursion(n);

    <span style="color:#000088">if</span> (result == -<span style="color:#006666">1</span>)
        <span style="color:#000088">printf</span>(<span style="color:#009900">"Input Error!\n"</span>);
    <span style="color:#000088">else</span>
        <span style="color:#000088">printf</span>(<span style="color:#009900">"Result is <span style="color:#4f4f4f">%d</span>\n"</span>, result);

    <span style="color:#000088">return</span> <span style="color:#006666">0</span>;
}</code></span>

时间复杂度为O(2^n) 
空间复杂度为O(n)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值