数据结构时间复杂度的概念

时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法执行所消耗的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。一个算法所花费的时间与其中语的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。

即:找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。

请计算一下Func1中++count语句总共执行了多少次?

void Func1(int N)
{
    int count = 0;
    for(int i = 0;i < N;++i)
    {
        for(int j = 0;j < N;++j)
        {
            ++count;
        }
    }
    
    for(int k = 0;k < 2 * N;++k)
    {
        ++count;
    }
    int M = 10;
    while(M--)
    {
        ++count;
    }
    printf("%d
",count);
}

 它的复杂度是跟一个N相关的函数式

从上往下看,第一个循环外面循环N次,里面也循环N次,所以第一个循环复杂度也就是F(N)=N*N

第二个循环是循环2倍的N,所以也就是2*N,加上第一个循环,复杂度也就是F(N)=N*N+2*N

第三个循环,因为M是确定的,所以加10就可以了,复杂度F(N)=N*N+2*N+10

算出来这个表达式以后再去给他值

当N是10的时候,F(N)=10^2+2*10+10=130

当N是100的时候,F(N)=100^2+2*100+10=10210

当N是1000的时候,F(N)=1000^2+2*100+10=1002010

把表达式简化,只关注影响最大的项,也就是F(N)=N^2,N越大,2*N+10对结果的影响越小,N^2的占比也越大

实际中我们计算空间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。

先计算精确的,再计算对结果影响最大的保留下来就行(不是这个准确的值,而是这个值的估算)

那么Func1的时间复杂度:O(N^2)

大O的渐进表示法

大O符号(Big O notation):是用于描述函数渐进行为的数学符号。推到大O阶方法:

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

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

3.如果高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶

//计算实Func2的时间复杂度?
void func2(int N) {
    int count = 0;
    for (int k = 0; k < 2 * N; ++K) {
        ++count;
    }
    int M = 10;
    while (M--) {
        ++count;
    }
    printf("%d
", count);
}

这题的时间复杂度为O(N),精确的算法是2N+10,N越大10对结果的影响也就越小,当N无限大的时候2N和N是一个级别,像上面说的,高阶项存在且不是1,所以去除这个相乘的常数所以为O(N)

计算Func3的时间复杂度?

void Func3(int N, int M) {
    int count = 0;
    for (int k = 0; k < M; ++k) {
        ++count;
    }
    for (int k = 0; k < N; ++K) {
        ++count;
    }
    printf("%d
", count);
}

这段代码的时间复杂度是O(M+N)

一般情况下时间复杂度计算时间未知数都是用的N但是也可以是M、K、X等等其它的

如果M远大于N,结果就是O(M)

如果N远大于M,结果就是O(N)

如果M和N差不多大,结果就是O(M)或O(N)

没有说名M和N的大小关系,结果就是O(M+N)

计算Func4的时间复杂度?

void Func4(int N) {
    int count = 0;
    for (int k = 0; k < 100; ++k) {
        ++count;
    }
    printf("%d
", count);
}

这题的时间复杂度为O(1)

因为没有未知数了,K<100是个常数,也就是把常数换成1,所以结果是O(1)

O(1)不是代表算法运行一次,是常数次

计算strchr的时间复杂度?
 

const char * strchr (const char * str, int character);

这段代码是查找字符的库函数

while(*str)
{
    if(*str==character)
    {
        return str;
    }
    else
        ++str;
}

最好的情况是1,平均的情况是N/2,最坏的情况是N

当一个算法随着输入不同,时间复杂度不同,时间复杂做悲观预期,看最坏的情况,所以是O(N)

计算冒泡排序的时间复杂度?

void bubblesort(int *a, int n) {
    assert(a);
    for (size_t end = n; end > 0; --end) {
        for (size_t i = 1; i < end; ++i) {
            if (a[i - 1] > a[i]) {
                Swap(&a[i - 1], &a[i])
                exchange = 1;
            }
        }
        if (exchange == 0)
            break;
    }

等差数列,比较次数是N-1,N-2,N-3……

精确的时间复杂度是F(N)=N*(N-1)/2

N阶数不一样,我们只去看阶数最高的那一项,所以时间复杂度是:O(N^2) 

int Binarysearch(int *a,int n,int x)
{
    assert(a);
    int begin=0;
    int end=n;
    while(begin<end)
    {
        int mid=begin+((end-begin)>>1)
        if(a[mid]<x)
            begin=mid+1;
        else if(a[mid]>x)
            end=mid;
        else
            return mid;
    }
    return -1;
}

这个时间复杂度是O(log以2为底N次方) O(log2N)

假设我们要查找x次,因为每次都折半也就是2^x次,也就是x=log2N