学会理解编程算法的复杂度

一、 如何评价一个算法的好坏 ,可以从时间与空间两方面来评判,解决相同的一个问题,不同的算法所用的时间和空间会有所差异,即:用时越短,占用空间越少的算法,则被称为好的算法。

二、复杂度分为:时间复杂度和空间复杂度

时间复杂度:算法所需要的时间越少,他的时间复杂度越低,但是由于现阶段硬件的种类多且不同,我们并不能精确且公平的测量算法程序运行的时间。因此,时间复杂度可以根据算法的基本操作的执行次数来判断。

空间复杂度:就是程序运行时所占用的空间大小(包括临时空间)这里我们暂且不做过多的讨论。

三、举例理解时间复杂度:

eg1 数组nums包含从0到n的所有整数,但其中缺了一个。

请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?

https://leetcode-cn.com/problems/missing-number-lcci/

方法一:公式计算

先将不缺失的0 ~ N 共N个数求和 为大和,再将缺失的0 ~ N 共N-1个数求和 为小和

最后大和去小和,就得到了缺失的数字。

(方法一:其基本操作共执行了 2n 次  , 其时间复杂度为 2n )

方法一:公式计算
int miss4(int* arr, int shu)
{
    int sum = 0;
    for (int i = 0; i <= shu; i++)     //这个循环执行了 n 次(假定数列中共n个数)
    {
        sum += i;                   
    }
    for (int i = 0; i < shu; i++)      //这个循环执行了 n 次
    {
        sum -= arr[i];
    }
    return sum;
}

方法二:排序+比较查找

先将缺失数列 进行从小到大排序,与第一种方法类似,再将0~N的每一位与排好序的缺失数列每一位进行对比如果不相同则说明,这一位的数字缺失。(因为所对比的是0~N的定序数列,所以需要将原数列排序,达到一 一对应的效果)

此法的时间复杂度为:(n+(n-1)+(n-2)+(n-3)+…+1)+ n   (排序的执行次数 + 遍历查找次数)

                        即为:  (n^2+n) / 2 + n;

int miss(int* arr, int shu)
{
    for (int i = 0; i < shu - 1; i++)      //冒泡排序
    {
        for (int j = 0; j < shu - 1 - i; j++)
        {
            if (arr[j] > arr[j + 1])
            {
                int tem = 0;
                tem = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tem;
            }
        }
    }
    int k = -1;
    for (int i = 1; i <= shu; i++)          //遍历查找
    {
        if (arr[i - 1] != (i - 1))
        {
            k = i - 1;
            break;
        }
    }
    if (k == -1 && arr[0] > 1)
    {
        return arr[0] - 1;
    }
    else if (k == -1)
        return arr[shu - 1] + 1;
    return k;
}

eg2: 二分查找

二分查找的时间复杂度是: logN (以2为底的log N)

计算方式如下:二分查找是每次查找筛选掉一半的数据,所以设数据总数为:N

精确时间复杂度:    1 = N / 2 / 2 / 2 / 2 / 2 / ……;  ==》    2^x = N     (2的 x 次幂等于N)

这里的 x 相当于查找的次数:因为,每查找一次,下一次查找的数据数量就除以二

所以上式中 :x = logN (这种写法便于书写与理解)  即, 时间复杂度是:logN


 char arr[] = { 1,2,3,4,5,6,7,9,10};
    int left = 0;                      //定义首元素下标
    int right = sizeof(arr) - 1;       //定义尾元素下标
    int mid = 0;                       //定义中间元素下标
    int a = 0;
    scanf("%d", &a);                   //输入要查找的值
    for (left = 0; left <= right;)     //开始查找
    {                                  
        mid = left+(right-left)/2;     //求出中间元素下标
        if (a < arr[mid])              //将输入值与中间元素对比
        {
            right = mid - 1;           //若小于中间元素,则向左缩小范围,即向左改变右下标
        }
        else if(a>arr[mid])
        {
            left = mid + 1;            //同理,向右改变左下标
        }
        else
        {
            printf("找到了");            
            break;                     //若找到,则跳出循环
        }
    }
    if (left > right)                  //判断是否完全查找完毕

————————————————
版权声明:本文为CSDN博主「MT_125」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/MT_125/article/details/122525067

四、复杂度表示

大O的渐进表示法(其主要思想是:用极限的思维,来去掉精确时间复杂度式子中对结果影响不大的项,进而简明的估算时间复杂度,使其易于观察和比较)

1.用常数 1 来表示运行时的所有加法常数

即如果一个算法的精确时间复杂度是能用 一个常数表示出来,那么,用大O的渐进表示法就表示为:    O(1)

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

如;上题中方法二的算法:精确的时间复杂度为 (n^2+n) / 2 + n  ;

项式中的  +n ,它对算法的影响随着计算次数的增加,越来越小,所以可以忽略不计。

所以用大O表示法表示为: O ( n^2 )

3.如果最高项存在且不是 1 ,则除去这一项的系数 。

如下算法:

他的精确时间复杂度表示为: 2 * m + n ;

int keep(int m ,int n)
{
    int count = 0;
    for(int i = 0;i < 2*m ;i++)
    {
        count++;
    }
    for(int i = 0; i< n ;i++)
    {
        count++;
    }
    return count;
}

如果用大O表示法:(如果没有对m 和 n 的明确说明,则表示为O(m+n))

① m 与 n 相差不大

表示为:O(m)      //2m+n可以近似为3m ,然后再将系数去掉

② m 远大于 n 

表示为:O (m)

③m 远小于 n 

 

表示为 O( n )

常用时间复杂度对比:

五、总结

1. 时间复杂度的用处是用来迅速判断一个算法的优劣,其运用的是底线思维,取得是最复杂时的情况。

2. 大O渐进表示法可以更加简明的表示时间复杂度,

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值