C语言数据结构时间复杂度

时间复杂度作用 

算法效率的目的是看算法实际是否可行,当同一问题存在多种算法时,可进行时间复杂度和空间性能的比较,以便从中挑选出较优算法。

时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算 机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。

时间复杂度的定义

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

影响算法的时间代价最主要因素是问题规模,问题规模是算法求解问题输入量的多少,是问题大小的本质表示,一般用整数n表示,n越大算法的执行时间越长。

Func1 执行的基本操作次数 :F(N)=N^2+2*N+10

                     N = 10 F(N) = 130 N = 100 F(N) = 10210 N = 1000 F(N) = 1002010  

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

大O的渐进表示法:

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

推导大O阶方法:

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

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

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

使用大O的渐进表示法以后,Func1的时间复杂度为:O(N^2)

通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。 另外有些算法的时间复杂度存在最好,平均和最坏情况:

最坏情况:任意输入规模的最大运行次数(上界) 平均情况:任意输入规模的期望运行次数

最好情况:任意输入规模的最小运行次数(下界) 例如:在一个长度为N数组中搜索一个数据x 最好情况:1次找到 最坏情况:N次找到

平均情况:N/2次找到

在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N) 

常见时间复杂度计算举例

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

基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N)

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

基本操作执行了M+N次,有两个未知数M和N,时间复杂度为 O(N+M)

int BinarySearch(int* a, int n, int x)
{
 assert(a);
 
 int begin = 0;
 int end = n-1;
 // [begin, end]:begin和end是左闭右闭区间,因此有=号
 while (begin <= end)
 {
 int mid = begin + ((end-begin)>>1);
 if (a[mid] < x)
 begin = mid+1;
 else if (a[mid] > x)
 end = mid-1;
 else
 return mid;
 }
 
 return -1;
}

二分查找算法的步骤如下:

  1. 确定查找范围:开始时查找范围是整个数组。

  2. 计算中间位置:计算查找范围内中间位置的元素索引。

  3. 比较中间元素:将中间元素与目标值进行比较。

  4. 调整查找范围

    • 如果中间元素正好是目标值,则查找成功。
    • 如果目标值小于中间元素,则在数组的左半部分(中间元素之前的部分)继续查找。
    • 如果目标值大于中间元素,则在数组的右半部分(中间元素之后的部分)继续查找。
  5. 重复步骤2到4:在新范围内重复这过程,直到找到目标值或范围为空。

  6. 结束查找:当查找范围为空时,表示整个数组已经搜索完毕,目标值不存在于数组中。

 时间复杂度一般都是最坏情况,二分查找法最坏的情况是数组中只剩下一个元素或者找不到元素,在这之前,二分查找法会不断缩小查找范围,即查找范围不断变为原来的1/2。

查找区间不断变化,N/2/2/2〰〰,查找了多少次就是除以几个2,假设循环走了x次,就是x个2相乘,即2^x=N,循环次数x=logN。所以二分查找法的时间复杂度为O(logN)。

// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{
 if(0 == N)
 return 1;
 
 return Fac(N-1)*N;
}

在递归中,从N到(N-1)最后到0次一共调用了N+1次,由于时间复杂度省略常数,所以递归的时间复杂度为O(N)。总的来说递归时间复杂度就是所有的递归调用次数累加。

long long Fib(size_t N)
{
 if(N < 3)
 return 1;
 
 return Fib(N-1) + Fib(N-2);
}

斐波那契数列的时间复杂度fib(N)到fib(N-1)和fib(N-2),一个变两个,两个变四个,直到出现fib1和fib2,第一层一个有2^0,第二层有2^1个,第三层有2^2个,最后一层有2^n-2个,累计调用次数为2^0+2^1+……+2^(N-2),为等比数列,总和为2^(N-1)-1次,省略常数,斐波那契数列的时间复杂度为O(2^N)。注意:O(2^N)只有理论意义,实践中太慢了。

  • 26
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值