算法基础(一) —— 时间复杂度研究(一)

版本记录

版本号 时间
V1.0 2018.09.14

前言

关于算法学习有很多很基础的概念和理论,我们不需要强行记忆但是一定要理解明白和说的出来,这个专题就是专门进行有关算法基本内容的一些解析。

时间复杂度

首先一起来理解一下时间复杂度。

个人理解:时间复杂度的计算并不是计算程序具体运行的时间,而是算法执行语句的次数。

官方解释:计算机科学中,算法的时间复杂度是一个函数,它定性描述了该算法的运行时间。这是一个关于代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,它考察当输入值大小趋近无穷时的情况。


常见的时间复杂度

常见的时间复杂度主要有下面几种:

  • 常数阶O(1)
  • 对数阶O(log2 n)
  • 线性阶O(n)
  • 线性对数阶O(n log2 n)
  • 平方阶O(n^2)
  • 立方阶O(n^3)
  • k次方阶O(n^K)
  • 指数阶O(2^n)

随着n的不断增大,时间复杂度不断增大,算法花费时间越多。

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

Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2^n)<Ο(n!)

计算方法

  • 选取相对增长最高或者说最快的项(对应的就是函数图像中增长最快的函数,比如3次方就比2次方的增长的快)。
  • 最高项系数是都化为1 。
  • 若是常数的话,因为执行语句次数固定,比如就是执行100次,那么就用O(1)表示 。

下面看几个计算方法示例。

1. O(1)

当算法执行固定的次数(即使有成千上万的数量级),和n没有关系的时候,那么复杂度就是O(1)。例如:

int x = 1;
while (x <= 10000)
{
    x++;
}

这里,while里面的代码就执行10000次,看着很多,但是对于机器的主频来说真不算什么,这个和n没有任何关系,就是一个固定的常数。

2. O(n^2)

这个一般是和循环嵌套有关系,而且复杂度会随着n的改变而变化。例如:

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

该算法for循环,最外层循环每执行一次,内层循环都要执行n次,执行次数是根据n所决定的,时间复杂度是O(n^2)

3. O(n)

这个很明显,就是时间复杂度是n线性相关的,例如:

for (i = 0; i < n; i ++)
{
    //执行语句
}

这个for循环执行次数与n有关,并且是线性的,所以复杂度就是O(n)。

4. O(log2 n)

下面看一下这个二分查找算法。

二分查找算法 - 非递归

int BinarySearch(int arr[], int len, int num)
{
    assert(arr);

    int left = 0;
    int right = len - 1;
    int mid;

    while (left <= right)
    {
        mid = left + (right - left) / 2;

        if (num > arr[mid])
        {
            left = mid + 1;
        }
        else if (num < arr[mid])
        {
            right = mid - 1;
        }
        else
        {
            return mid;
        }
    }

    return -1;
}

这个时间复杂度为O(log2 n) ,因为变量值创建一次,所以空间复杂度为O(1)

二分查找算法 - 递归

int BinarySearchRecursion(int arr[5], int lef, int rig,int aim)
{
    int mid = lef + (rig - lef) / 2;


    if (lef <= rig)
    {
        if (aim < arr[mid])
        {
            rig = mid - 1;
            BinarySearchRecursion(arr, lef, rig, aim);
        }
        else if (arr[mid] < aim)
        {
            lef = mid + 1;
            BinarySearchRecursion(arr, lef, rig, aim);
        } 
        else if (aim == arr[mid])
        {
            return mid;
        }

    }
    else
        return -1;

}

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

可能有人想知道这个时间复杂度O(log2 n)是怎么计算出来的?

第几次查询 剩余待查询元素数量
1 N/2
2 N/(2^2)
3 N/(2^3)
... ...
K N/(2^K)

从上表可以看出N/(2^K)肯定是大于等于1,也就是N/(2^K)>=1,我们计算时间复杂度是按照最坏的情况进行计算,也就是是查到剩余最后一个数才查到我们想要的数据,也就是

N/(2^K)=1  => N=2^K  => K=log2N 

所以二分查找的时间复杂度为O(log2N)

5. O(2^n)

下面看一下这个斐波那契数列(Fibonacci sequence),又称黄金分割数列。

int FeiBoNaCciRecursion(int num)
{
    if (num < 0)
        return -1;
    if (num <= 2 && num > 0)
        return 1;
    else
        return FeiBoNaCciRecursion(num - 1) + FeiBoNaCciRecursion(num - 2);

}

int main()
{
    int n;
    int result;

    printf("Input n\n");
    scanf("%d", &n);

    result = FeiBoNaCciRecursion(n);

    if (result == -1)
        printf("Input Error!\n");
    else
        printf("Result is %d\n", result);

    return 0;
}

在这种递归情况下的时间复杂度就是O(2^n)

在分析算法的时间复杂度的时候,非递归使用的是for循环,其时间复杂度为O(n)。而递归的时间复杂度则比较复杂,其分析出来为O(2^n)。这里需要说明的就是,非递归的for循环其时间复杂度O(n)虽然很小,但是其空间复杂度却比递归调用差得多。因为,循环在每次循环的时候,都把相应的数值保存下来了,而递归调用却不会保存相应的数值。


算法复杂度练习

下面我们就做一些练习计算算法复杂度。

1. 例1

int main(int argc, const char * argv[])
{
    int number = 0;
    int n = 999;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= i; j++){
            for(int k = j; k <= j; k++){
                number ++;
            }
        }
    }

    return 0;
}

我们抽象出来公式然后进行近似计算,如下所示:

那么这个算法的时间复杂度就是O(n^3)

2. 例2

int main(int argc, const char * argv[])
{
    int n = 6000;
    int i = 1;
    while(i <= n){
        i = i * 3;
    }
    printf("i = %d\n", i);
    return 0;
}

下面看一下上面的时间复杂的,这个可以看做是进行迭代的。假设执行了k次之后,才会停止,那么就有i = n,此时i = 3*3*3*3..........*1 = 3^k,因为这是一个递归,所以有3^k = n,两边取对数,那么就有k = log n(底数为3),此时就是T(n) = O(log n)

3. 例3

int main(int argc, const char * argv[])
{
    int n = 10;
    int i = 0, j = 1;
    while(i + j <= n){
        printf("i + j = %d\n", i + j);
        if (i > j) {
            j++;
        }
        else {
            i++;
        }
    }
    return 0;
}

每次循环,i + j的值都会增加1,一共循环了n次,所以时间复杂度就是O(n),可以看一下输出:

i + j = 1
i + j = 2
i + j = 3
i + j = 4
i + j = 5
i + j = 6
i + j = 7
i + j = 8
i + j = 9
i + j = 10

4. 例4

int main(int argc, const char * argv[])
{
    int x = 91, y = 100;
    while (y > 0) {
        if (x > 100) {
            x -= 10;
            y--;
        }
        else {
            x++;
        }
    }
    return 0;
}

这里可以看见,总共循环了很多次,但是我们没有看见和n有关系,也就是说和n是无关的,就是一个常数阶的函数,时间复杂度为O(1)

参考文章

1. 时间复杂度和空间复杂度的简单讲解
2. 算法 二分查找的时间复杂度为O(log2N)的原因推理
3. 复杂度分析之斐波那契数列

后记

本篇主要讲述了算法的时间复杂度分析,感兴趣的给个赞或者关注~~~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,427评论 6 508
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,551评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 165,747评论 0 356
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,939评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,955评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,737评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,448评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,352评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,834评论 1 317
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,992评论 3 338
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,133评论 1 351
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,815评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,477评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,022评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,147评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,398评论 3 373
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,077评论 2 355

推荐阅读更多精彩内容