【数据结构】时间复杂度

目录

一.算法时间复杂度的定义

二.大O阶渐进表示法

三.常见的时间复杂度

四.轮转数组


一.算法时间复杂度的定义

定义:在计算机科学中,算法的时间复杂度是一个函数式T(N),它定量描述了该算法的运行时间。时间复杂度是衡量程序的时间效率,那么为什么不去计算程序的运行时间呢?

1. 因为程序运行时间和编译环境和运行机器的配置都有关系,比如同⼀个算法程序,用一个老编译器进行编译和新编译器编译,在同样机器下运行时间不同。

 2. 同⼀个算法程序,用⼀个老低配置机器和新高配置机器,运行时间也不同。

 3. 并且时间只能程序写好后测试,不能写程序前通过理论思想计算评估。

那么算法的时间复杂度是⼀个函数式T(N)到底是什么呢?这个T(N)函数式计算了程序的执行次数。假设每句指令执行时间基本⼀样(实际中有差别,但是微乎其微),那么执行次数和运行时间就是等比正相关, 这样也脱离了具体的编译运行环境。执行次数就可以代表程序时间效率的优劣。比如解决⼀个问题的算法a程序T(N)=N,算法b程序T(N)=N^2,那么算法a的效率一定优于算法b。

总结:一个算法所花费的时间与其中语句的总执行次数T(n)成正比例,算法中的基本操作的执行次数,为算法的时间复杂度.


二.大O阶渐进表示法

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

推导规则:

1.时间复杂度函数式T(N)中,只保留最高阶项,去掉那些低阶项,因为当N不断变大时, 低阶项对结果影响越来越小,当N无穷大时,就可以忽略不计了。

2. 如果最高阶项存在且不是1,则去除这个项目的常数系数,因为当N不断变大,这个系数对结果影响越来越小,当N无穷大时,就可以忽略不计了。

3. T(N)中如果没有N相关的项目,只有常数项,用常数1取代所有加法常数。

简而言之,就是:

1.在修改后的函数中,只保留最高阶项.

2.用1取代函数中的所有加法常数..

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


三.常见的时间复杂度

1.

// 计算Func1的时间复杂度? 
void Func1(int N) 
{ 
    int count = 0; //1
    for (int k = 0; k < 100; ++ k) 
    { 
         ++count; //100
    } 
    printf("%d\n", count); //1
}

可以看出这个函数的运行次数是101次。

根据大O阶渐进表示法:.用1取代函数中的所有加法常数,而且它并没有最高阶项,所以时间复杂度是O(1).

只要这串代码中没有N,就是常数阶,时间复杂度就是O(1).

2.

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

可以得出:T(N)=2N+10

根据大O阶渐进表示法,在修改后的函数中,只保留最高阶项.如果最高阶项存在且不是1,则去除与这个项目相乘的常数.

得到,O(N).

3.

// 计算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\n", count); 
}

可以得出,T (N) = M + N,由于M,N都是未知数,都不知道具体大小,那我们都看为N,所以T(N)=2N

根据大O阶渐进表示法,在修改后的函数中,只保留最高阶项.如果最高阶项存在且不是1,则去除与这个项目相乘的常数.

得到,O(N)

4.

// 计算strchr的时间复杂度? 
const char * strchr ( const char * str, int character)
{
 const char* p_begin = s;
 while (*p_begin != character)
 {
 if (*p_begin == '\0')
 return NULL;
 p_begin++;
}
 return p_begin;
}

strchr执行的基本操作次数:若要查找的字符在字符串第一个位置,则: T (N) = 1;

若要查找的字符在字符串最后的一个位置, 则:T (N) = N  ;

若要查找的字符在字符串中间位置,则:T (N) =  N / 2 ;

因此:strchr的时间复杂度分为:最好情况:O(1) 最坏情况:O(N) 平均情况:O(N)

通过上面我们会发现,有些算法的时间复杂度存在最好、平均和最坏情况。 最坏情况:任意输入规模的最大运行次数(上界); 平均情况:任意输入规模的期望运行次数;最好情况:任意输入规模的最小运行次数(下界)。 大O的渐进表示法在实际中一般情况关注的是算法的上界,也就是最坏运行情况。

5.

// 计算BubbleSort的时间复杂度? 
void BubbleSort(int* a, int n) 
{ 
 assert(a); 
 for (size_t end = n; end > 0; --end) 
 { 
     int exchange = 0; 
     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; 
     }     
}

这个运行次数不太好看出,我们可以列一个表格辅助做题:

i的值内循环的次数
i=0n
i=1n-1
i=2n-2
......
i=n-22
i=n-11

将i从0到n-1的每个内循环执行的次数加起来可得总的执行次数为:n+(n-1)+(n-2)+...+2+1=n*(n+1)/2=n^2/2+n/2.(等差数列求和)

根据大O阶推导方法可得,这段代码的时间复杂度为O(n^2).

6.

void func5(int n)
{
 int cnt = 1;
 while (cnt < n)
 {
     cnt *= 2;
 }
}

因为每次cnt乘以2之后,就距离n更近了一分.cnt与多少个2相乘后大于n就会退出循环.

设循环内基本操作的循环次数为x,可得2^x=n,即x=\log_{2}n.

因此这个循环的时间复杂度为O(logn)

当n接近无穷大时,底数的大小对结果影响不大。因此,一般情况下不管底数是多少都可以省略不写,即可以表示为log n不同书籍的表示方式不同,我们建议使用log n

jkhjk\
标题


四.轮转数组

https://leetcode.cn/problems/rotate-array/description/

思路一:

时间复杂度:O(n )

循环K次将数组所以元素向后移动一位(代码不通过)

void rotate(int* nums, int numsSize, int k) {
 while(k--)
 {
    int end = nums[numsSize-1];
    for(int i = numsSize - 1;i > 0 ;i--)
    {
        nums[i] = nums[i-1];
    }
    nums[0] = end;
 }
}


思路二:

空间复杂度O(n):申请新数组空间

先将后k个数据放到新数组中,再将剩下的数据挪到新数组中

void rotate(int* nums, int numsSize, int k) 
{
    int newArr[numsSize];
    for (int i = 0; i < numsSize; ++i) 
    {
        newArr[(i + k) % numsSize] = nums[i];
    }
    for (int i = 0; i < numsSize; ++i) 
    {
    nums[i] = newArr[i];
    }
}


思路三:

空间复杂度O(1)

• 前n-k个逆置:4321567

 • 后k个逆置:4321765

 • 整体逆置:5671234

void reverse(int* nums,int left,int right)
{
 while(left<right){
 int tmp = nums[left];
 nums[left] = nums[right];
 nums[right] = tmp;
 left++;
 right--;
 }
}
void rotate(int* nums, int numsSize, int k)
{
 k = k%numsSize;
 reverse(nums,0,numsSize-k-1);
 reverse(nums,numsSize-k,numsSize-1);
 reverse(nums,0,numsSize-1);
}


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值