数据结构之算法时间复杂度

原创 2017年09月10日 21:12:23

数据结构之算法时间复杂度

原文链接
算法的时间复杂度定义为:

在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,记作:T(n}=0(f(n))。它表示随问题规模n的增大,算法执行时间的埔长率和 f(n)的埔长率相同,称作算法的渐近时间复杂度,简称为时间复杂度。其中f( n)是问题规横n的某个函数。

根据定义,求解算法的时间复杂度的具体步骤是:

  • 找出算法中的基本语句

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

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

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

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

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

如何推导大o阶呢?我们给出了下面 的推导方法:

1.用常数1取代运行时间中的所有加法常数。

2.在修改后的运行次数函数中,只保留最髙阶项。

3.如果最高阶项存在且不是1,则去除与这个项相乘的常数。

简单的说,就是保留求出次数的最高次幂,并且把系数去掉。 如T(n)=2n^2+n+1 =O(n^2)

例如:

#include "stdio.h"

int main()
{
    int i, j, x = 0, sum = 0, n = 100;  /* 执行1次 */
    for( i = 1; i <= n; i++)    /* 执行n+1次 */
    {
        sum = sum + i;               /* 执行n次 */   
        for( j = 1; j <= n; j++)    /* 执行n*(n+1)次 */
        {
            x++;                /* 执行n*n次 */
            sum = sum + x;      /* 执行n*n次 */
        }
    }
    printf("%d", sum);          /* 执行1次 */
}

按照上面推导“大O阶”的步骤,我们来看
第一步:“用常数 1 取代运行时间中的所有加法常数”,
则上面的算式变为:执行总次数 =3n^2 + 3n + 1
(直接相加的话,应该是T(n) = 1 + n+1 + n + n*(n+1) + n*n + n*n + 1 = 3n^2 + 3n + 3。现在用常数 1 取代运行时间中的所有加法常数,就是把T(n) = 3n^2 + 3n + 3中的最后一个3改为1. 就得到了 T(n) = 3n^2 + 3n + 1)

第二步:“在修改后的运行次数函数中,只保留最高阶项”。
这里的最高阶是 n 的二次方,所以算式变为:执行总次数 = 3n^2

第三步:“如果最高阶项存在且不是 1 ,则去除与这个项相乘的常数”。
这里 n 的二次方不是 1 所以要去除这个项的相乘常数,算式变为:执行总次数 = n^2

因此最后我们得到上面那段代码的算法时间复杂度表示为: O( n^2 )

面我把常见的算法时间复杂度以及他们在效率上的高低顺序记录在这里,使大家对算法的效率有个直观的认识。

O(1) 常数阶 < O(logn) 对数阶 < O(n) 线性阶 < O(nlogn) < O(n^2) 平方阶 < O(n^3) < { O(2^n) < O(n!) < O(n^n) }

最后三项用大括号把他们括起来是想要告诉大家,如果日后大家设计的算法推导出的“大O阶”是大括号中的这几位,那么趁早放弃这个算法,在去研究新的算法出来吧。因为大括号中的这几位即便是在 n 的规模比较小的情况下仍然要耗费大量的时间,算法的时间复杂度大的离谱,基本上就是“不可用状态”。

一 计算 1 + 2 + 3 + 4 + …… + 100。
常规算法,代码如下:

#include "stdio.h"

int main()
{
    int i, sum = 0, n = 100;    /* 执行1次 */
    for( i = 1; i <= n; i++)    /* 执行 n+1 次 */
    {
        sum = sum + i;          /* 执行n次 */
        //printf("%d \n", sum);
    }
    printf("%d", sum);          /* 执行1次 */
}

代码附加的注释可以看到所有代码都执行了多少次。那么这写代码语句执行次数的总和就可以理解为是该算法计算出结果所需要的时间。该算法所用的时间(算法语句执行的总次数)为: 1 + ( n + 1 ) + n + 1 = 2n + 3

而当 n 不断增大,比如我们这次所要计算的不是 1 + 2 + 3 + 4 + …… + 100 = ? 而是 1 + 2 + 3 + 4 + …… + n = ?其中 n 是一个十分大的数字,那么由此可见,上述算法的执行总次数(所需时间)会随着 n 的增大而增加,但是在 for 循环以外的语句并不受 n 的规模影响(永远都只执行一次)。所以我们可以将上述算法的执行总次数简单的记做: 2n 或者简记 n

这样我们就得到了我们设计的算法的时间复杂度,我们把它记作: O(n)

二 求两个n阶方阵C=A*B的乘积其算法如下:

    void MatrixMultiply(int A[n][n],int B [n][n],int C[n][n])

      {

   (1) for(int i=0; i <n; i++)                       //n+1

         {

   (2)      for (j=0;j < n; j++)                       //n*(n+1)

              {

   (3)           C[i][j]=0;                                  //n^2

   (4)           for (k=0; k<n; k++)                 //n^2*(n+1)

                  {

   (5)              C[i][j]=C[i][j]+A[i][k]*B[k][j]; //n^3

                 }

             }

         }

     }

则该算法所有语句的频度之和为:

T(n) = 2n^3+3n^2+2n+1; 利用大O表示法,该算法的时间复杂度为O(n^3)。

三 分析下列时间复杂度


void test_(int n)
{
    i = 1, k = 100;
    while (i<n)
    {
        k = k + 1;
        i += 2;
    }
}

设for循环语句执行次数为T(n),则 i = 2T(n) + 1 <= n - 1, 即T(n) <= n/2 - 1 = O(n)

四 分析下列时间复杂度


void test_2(int b[], int n)
{
    int i, j, k;
    for (i=0; i<n-1; i++)
    {
        k = i;
        for (j=i+1; j<n; j++)
        {
            if (b[k] > b[j])
            {
                k = j;
            }
        }
        x = b[i];
        b[i] = b[k];
        b[k] = x;
    }
}

其中,算法的基本运算语句是
if (b[k] > b[j])
{
k = j;
}
其执行次数T(n)为:
这里写图片描述

五 分析下列时间复杂度

void test_3(int n)
{
    int i = 0, s = 0;
    while (s<n)
    {
        i++;
        s = s + i;
    }
}

其中,算法的基本运算语句即while循环内部分,
设while循环语句执行次数为T(n),则
这里写图片描述

六 Hanoi(递归算法)时间复杂度分析

void hanoi(int n, char a, char b, char c)
{
    if (n==1)
    {
        printf("move %d disk from %c to %c \n", n, a, c);  //执行一次
    }
    else
    {
        hanoi(n-1, a, c, b);    //递归n-1次
        printf("move %d disk from %c to %c \n", n, a, c);  //执行一次
        hanoi(n-1, b, a, c);    //递归n-1次
    }
}

对于递归函数的分析,跟设计递归函数一样,要先考虑基情况(比如hanoi中n==1时候),这样把一个大问题划分为多个子问题的求解。
故此上述算法的时间复杂度的递归关系如下:
这里写图片描述

这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述
这里写图片描述

常用排序算法时间复杂度

这里写图片描述

版权声明:本文为博主原创文章,转载请附带原文链接。

相关文章推荐

数据结构中对于时间复杂度的初步了解

首先先了解几个概念 时间复杂度 (1)时间频度 一个算法执行所耗费的时间,从理论上是不能算出来的,必须上机运行测试才能知道。但我们不可能也没有必要对每个算法都上机测试,只需知道哪个算法花费的时间多...

数据结构之线性表

数据结构之线性表线性表是一种常用的数据结构。在实际应用中,线性表都是以栈、队列、字符串、数组等特殊线性表的形式来使用的。由于这些特殊线性表都具有各自的特性,因此,掌握这些特殊线性表的特性,对于数据运算...

正斜杠(/)和反斜杠(\)的区别

(1)  windows 路径分隔符采用 反斜杠(\); URL采用 正斜杠(/)。如:                                                        ...

常用数据结构2——栈,实现PUSH、POP和取最小值操作算法时间复杂度为o(1)

看到一道笔试题,要求设计一个“栈”数据结构,使得对该栈的PUSH(进栈)、POP(出栈)以及取最小值(MIN)操作的时间复杂度为o(1)。我在网上搜了下,大家貌似也是用双栈来实现的。之所以说是双栈,是...

数据结构基础篇(2)--算法时间复杂度

算法的时间复杂度

数据结构-算法-时间复杂度计算

算法的时间复杂度定义为: 在进行算法分析时,语句总的执行次数T(n)是关于问题规模n的函数,进而分析T(n)随n的变化情况并确定T(n)的数量级。算法的时间复杂度,也就是算法的时间量度,记作:T(n}...

【数据结构】01.8种排序算法的稳定性和时间复杂度小结

选择排序、快速排序、希尔排序、堆排序不是稳定的排序算法, 冒泡排序、插入排序、归并排序和基数排序是稳定的排序算法。 冒泡法:   这是最原始,也是众所周知的最慢的算法了。他的...

数据结构与算法-如何计算时间复杂度

今天我们来谈一下如何计算时间复杂度。 时间复杂度概念:(百度版) 同一问题可用不同算法解决,而一个算法的质量优劣将影响到算法乃至程序的效率。算法分析的目的在于选择合适算法和改进算法。 计算机科学中,算...

数据结构——算法的时间复杂度

如果一个问题的规模是n,解这一问题的某一算法所需要的时间为T(n),它是n的某一函数 T(n)称为这一算法的“时间复杂性”。 当输入量n逐渐加大时,时间复杂性的极限情形称为算法的“渐近时间复杂性...

SQL性能优化中的底层概念,时间复杂度,算法和数据结构,数据库组成,查询优化和表关联原理.

原文地址: http://blog.jobbole.com/100349/ 一提到关系型数据库,我禁不住想:有些东西被忽视了。关系型数据库无处不在,而且种类繁多,从小巧实用的 SQLite 到强大...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:数据结构之算法时间复杂度
举报原因:
原因补充:

(最多只允许输入30个字)