算法之时间复杂度---数据结构

news2024/11/27 6:30:51

目录

前言:

1.时间复杂度

1.1时间复杂度的理解

1.2规模与基本操作执行次数

1.3大O渐进表示法

1.4计算基本操作的次数

2.常见的时间复杂度及其优劣比较


❤博主CSDN:啊苏要学习

    ▶专栏分类:数据结构◀

  学习数据结构是一件有趣的事情,希望读者能在我的博文切实感受到,数据之间存在的关系,在对数据元素进行操作的时候,能心中有数,脑中有画! 


前言:

  在计算机中,如何判断一个算法的好与坏,我们使用时间复杂度来评判。时间复杂度不是一个精确的值,而是大致描绘算法的时间效率。大家目前写的代码对于计算机来说都是可以很快就跑完的,因为都是比较简单的常数阶O(1)或线性阶O(n),但如果写了一个很差劲的算法,加上规模很大(数据多),计算机看了都要落泪。

1.时间复杂度

1.1时间复杂度的理解

  时间复杂度的概念,为什么需要有时间复杂度的概念?因为我们人在算术的时候,是需要耗费时间的;计算机在执行我们预设的指令也需要耗费时间,耗费时间长短就显的比较重要,在保证不错的前提下,我们期望计算机能很快跑出结果,而不用等上一段时间

  了解完时间复杂度的必要性,那我们如何算一个算法的时间复杂度呢?把一个程序放在计算机上跑起来,计算其用的时间?这个方法显然是不可行的,相信读者也想到了不同计算机,硬件的性能和软件的加持是不一样的;即使是同一台计算机,你也不能完全保证在执行过程中,每一次计算机的状态和前一次是完全一样的;

1.2规模与基本操作执行次数

   假设计算机在处理每一条语句的时间是一个计算机单位时间,我们通过计算算法里基本操作的执行次数,使用大O(有点像极限)表示法来表示

void Func(int N)
{
    int count = 0;
    for(int i = 0; i < N; i++)//执行n次
    {
        for(int j = 0; j < N; j++)
        {
            count++;//对于外层的每一次,里层都要执行n次
            //n*n
        }
    }
    for(int k = 0; k < 5N; k++)
    {
        count++;//5n
    }
    int a = 10;
    while(a--)
    {
        count++;//10
    } 
    printf("%d", count);
}

  这里面count这个基本操作被执行了几次呢?

  1. 第一部分是两层循环:执行了n^2次;
  2. 第二部分是一层for循环:执行了5n次
  3. 第三部分是while循环:执行了10次

  总共执行了N = n^2 + 5n + 10次,随着N(问题规模)的增大,执行的次数也会随之增大。我们来比较一下下面所列出的问题规模对应的需执行基本操作的次数:

  • N = 10 、n = 160
  • N = 100、n = 10510
  • N = 1000、n = 1005010

  随着问题规模的增大,n也在变大。

我们计算N = n^2的执行次数

N = 10、n = 100  

N = 100、n = 10000

N = 1000、n = 1000000

对比5n+10这部分,n^2对执行次数的贡献是最大的,我们只考虑n^2,时间复杂度就是O(N^2),这就是大O渐进表示法。随着规模的增大,对算法时间效率高低影响最大的那一部分,就是该算法的时间复杂度。     

 大O渐近表示法:我们只关注执行次数表达式中对结果影响最大的那一项,也就是最高阶。

1.3大O渐进表示法

  我们再通过分析代码来理解大O法表示时间复杂度以及总结大O表示的方法

void Fun(int N)
{
    int count = 0;
    for(int k = 0; k < 2N; k++)
    {
        count++;//2n次
    }
    int n = 10;
    while(n--)
    {
        count++;//10次
    }
}

  这段代码的执行次数是2n+10,时间复杂度是多少呢?大家可能说是O(2N),但真的是这样吗?其实应该是O(N)不妨这样想,对于次数起最大影响的是由n引起的,如果n为0,2n就为0,就只有固定的10次。而n从0-10000,次数也就越来越多的,前面的2改变次数的多和少还是取决于n的大小

  大O渐近表示法:对于最高次数的项,与之相乘或相除的常数都是可以省略的,省略后就是大O表示法

void Fun(int N, int M)
{
    int count = 0;
    for(i = 0; i < N; i++)
    {
        count++//n次
    }
    for(j = 0; j < M; j++)
    {
        count++;//m次
    }
}

  这里的时间复杂度是O(N+M),想表达的点是,我们习惯用N来表示执行的次数,不要因为习惯而阻碍了我们做出正确的判断这里N和M都是未知数,对基本操作的次数都有影响,所以自然就都得写在大O里面。大O表示法可以有两个未知数,甚至多个,视情况而定

  如果条件里有说N远大于M,时间复杂度就变成了O(N);如果说N和M差不读,时间复杂度为O(N)或O(M),因为N+M相当于2N或2M,最高项前面的常数影响不大

void Fun(int N)
{
    int count = 0;
    for(int i = 0; i < 100; i++)
    {
        count++;//100次
    }
}

  这里执行了100次,时间复杂度不是O(N),因为N我们没用到。那是不是O(100)呢?也不是,我们用1来表示大O表示法中的所有加法常数所以这段代码的时间复杂度是O(1),表示执行常数次,O(1)不是说算法执行1次的意思,就好像执行100次写成O(1),不是说只执行一次,这里强调的是,不管规模怎么变,变的多大,这个算法执行完常数次就结束,是最优的算法

大O渐近表示法:用常数1代替所有加法常数

大O表示法
最高阶对结果影响最大,关注最高阶。
最高阶的常数省略掉
所有加法常数都用数字1替代

  例子:N = 2n^2 + n + 10。直接看出来时间复杂度是N^2,我想读者们应该都get到了。给出我们基本操作的次数,我们就可以很轻松的用大O表示法表示出时间复杂度,这个并不难,重要的一点是算出或者看出算法的执行次数

1.4计算基本操作的次数

三种情况:

char* serch_ch(const char* str, char ch)
{
    //从字符数组的头开始往后找
    //在一个字符数组里查找一个想要查找的字符
    while(*str != '\0')
    {
        if(*str == ch)
            return str;
        str++;
    }
    return NULL;
}

  一个字符数组,长度为N,在字符数组里查找一个字符ch,到底需要查找多少次(基本执行次数)?

  • 最好的情况:第一个字符就是我们要查找的,只进行一次,时间复杂度为O(1)
  • 最差的情况:遍历完整个数组,可能在最后有这个字符,也可能没有,总之对长度为n的数组都找了一遍(基本操作次数:查找了n次),时间复杂度为O(N)。
  • 平均的情况:要查找的数是随机的,数组里放的字符也是随机的,从概论上来讲,我们会在接近中间的位置找到,执行n/2次,时间复杂度还是O(N)。

  当一个算法存在这三种情况的时候,我们看最坏的情况,做最坏的打算,为我们打底。如果读者看过修仙类的小说的话,我们一般会看到男主很厉害但又想着可能出现的最坏的情况,换句话说就是想的周全。

冒泡排序的基本操作次数:

  冒泡排序就是将一组数字给排成升序。说到冒泡,大家有没有想到生活中煮水器在煮水的时候,那个水泡从底部升起来,从小泡变成大泡。抽象理解小泡是小的数字,升起来的大泡就是大的数字。因为是计算机的知识,我们引进来让大家提个兴趣,不是来讲物理滴(doge)。

  一组数字 8 7 6 5 4 3 2 1。我们想排成1 2 3 4 5 6 7 8的升序序列。我们有思路、分析分析思路就可以了,可以不用敲代码就可以算出这个思路的时间复杂度

  因为这是一个长度为N的数字序列,将第一个数冒泡到最后,需要冒泡N次,这是第一趟冒泡第一趟冒泡的结果就是,把一个数字放到了它该放在的位置上,接下里就只有N-1个数字需要排序了了第二趟冒泡N-1次、第三趟冒泡N-2次、.......、第N趟冒泡1次总共冒泡的次数N + (N - 1) + (N - 2) +.......+ 2 + 1是一个等差数列求和,首项加尾项乘项数除2--- (N+1)*N/2就是冒泡排序的基本操作次数。 时间复杂度O(N^2)。 

二分查找的基本操作次数:

  二分查找是在一个有序的数组里查找想要的数字。现在有一个数组的元素排列是 1 2 3 4 5 6 7 8 9。

  如果不是,假设我们找的是9这个数字,这时候,因为这个数组是有序的,我们中间元素比要查找的小,中间下标左边的就不用再找了,left左下标变成指向mid+1的元素(就相当于少了一半的数据),再和right重新生成中间下标

  当left<=right的时候,就有元素可以找,当left大于了right,就找不到了,最后一次查找是在left = right的时候,此时无论找不找的到、都相当于把长度为N的有序数组给查遍了查了几次呢?由于我们每次查,数组的元素少一半,对于数组长度N,我们得出2的x次方等于N。这里可以这样想,假设这个数组有8个元素,我用二分查找,每次查找都会去掉一半的数据,请问查几次可以查完,2的3次方等于8,我们知道查3次就够了

  所以X是基本操作此时 X = log以2为底N的对数。时间复杂度是O(logN)。

  logN的意思是以2为底,N的对数,由于底数电脑上不太支持写出来,所以我们约定这样写。当然有些数还这样写lgN,实际上这个不合理的程度要大一点,我们尽量写成logN。

练习:递归求阶乘的时间复杂度:

long long Fac(size_t N)
{
    return N < 2 ? N : Fac(N-1) * N;
}

  这里使用递归实现求N的阶乘,这里的时间复杂度是多少呢?请看下面的画图:

   N为几,Fac()函数就被调用几次,每一次函数都进行三次基本操作(判断、得到值、返回)也就是说,Fac函数里的算术复杂度是O(1),而调用这个函数的时间复杂度是O(N)也就是执行了3N次基本操作,所以时间复杂度是O(N)

用for循环来理解也很容易,读者可以类比一下:

int main()
{
    int count = 0;
    int N = 0;
    scanf("%d", &N);
    for(i = 0; i < N; i++)//循环N次
    {
        for(j = 0; j < 3; j++)//i的每一个值都进行三次
        {
            count++;//3N次
        }
    }
    return 0;
}

  这里的循环N次就相当于递归调用是用了N次,每次里面都有三次基本操作,就是3N

  扩展话题:斐波那契数列的时间复杂度的计算

2.常见的时间复杂度及其优劣比较

  常见的时间复杂度有:O(N^2)、O(N)、O(logN)、O(1)这几种

  O(1)是最牛的时间复杂度,没有再厉害的了。O(logN)也是一个非常好的时间复杂度,它和O(N)是有个量级在的,我们来计算一下:

  比如遍历数组查找一个元素,恰巧要找的数组元素在末尾,当N = 1000的时候,O(N)的算法需要执行1000次基本操作、O(logN)的算法只需要10次就够了,因为2的10次方是1024

  当N = 1000000(百万),O(N)需要百万次,O(logN)只需要20次,因为1024*1024我们简单看成1000*1000就够了

  当N = 1000000000(十亿),O(logN)仅需要30次就足亦!

  讲到这里,我们就把时间复杂度的知识内容讲完啦。空间复杂度和算法的一些特性博主可能会另外出一篇,内容不多,而且空间复杂度和时间复杂度一样用大O表示法表示,也一样是估算,空间复杂度算的是程序额外占用空间的数量,比如创建一个int a;变量,占的数量就是1。


结语:希望读者读完能有所收获!对数据结构有进一步的认识!✔

  读者对本文不理解的地方,或是发现文章内容上有误等,请在下方评论留言告诉博主哟~,也可以对博主提出一些文章改进的建议,感激不尽!最后的最后!

  ❤求点赞,求关注,你的点赞是我更新的动力,一起进步吧。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/475075.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2023五一数学建模B题完整模型代码【原创首发】

已经完成五一数学建模全部内容&#xff0c;大家可以文末查看&#xff01;&#xff01;供参考使用&#xff01; 摘要 随着网络购物的普及和发展&#xff0c;快递行业需求持续增长&#xff0c;对于快递公司来说&#xff0c;准确预测运输需求以及合理规划运输线路和仓库布局变得…

分享一个有意思的键盘,我们就只会ctrl c+v

先上效果图&#xff1a; 再上代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title</title><style>* {border: 0;box-sizing: border-box;margin: 0;padding: 0;}:roo…

【pyTorch学习笔记④】PyTorch基础·中篇

文章目录 三、Numpy与Tensor3.Tensor的索引4.Tensor的广播机制5.逐元素操作6.归并操作7.比较操作8.矩阵操作9.PyTorch与Numpy的比较 相关推荐 三、Numpy与Tensor 3.Tensor的索引 &#xff08;1&#xff09;item&#xff1a;若Tensor为单元素&#xff0c;则返回标量&#xff0…

驱动开发:通过MDL映射实现多次通信

在前几篇文章中LyShark通过多种方式实现了驱动程序与应用层之间的通信&#xff0c;这其中就包括了通过运用SystemBuf缓冲区通信&#xff0c;运用ReadFile读写通信&#xff0c;运用PIPE管道通信&#xff0c;以及运用ASYNC反向通信&#xff0c;这些通信方式在应对一收一发模式的时…

【Java笔试强训 8】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;两种排…

【Java笔试强训 6】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;不要二 …

权限提升:本地权限提升.(AT || SC || PS )

权限提升&#xff1a;本地权限提升. 权限提升简称提权&#xff0c;由于操作系统都是多用户操作系统&#xff0c;用户之间都有权限控制&#xff0c;比如通过 Web 漏洞拿到的是 Web 进程的权限&#xff0c;往往 Web 服务都是以一个权限很低的账号启动的&#xff0c;因此通过 Web…

JavaScript用map实现Hashmap

&#x1f4dd;个人主页&#xff1a;爱吃炫迈 &#x1f48c;系列专栏&#xff1a;数据结构与算法 &#x1f9d1;‍&#x1f4bb;座右铭&#xff1a;道阻且长&#xff0c;行则将至&#x1f497; 文章目录 正如我们已经看到的&#xff0c;Map 对象是一个键值对。最初&#xff0c;M…

〖Python网络爬虫实战⑲〗- 数据存储之CSV文件

订阅&#xff1a;新手可以订阅我的其他专栏。免费阶段订阅量1000 python项目实战 Python编程基础教程系列&#xff08;零基础小白搬砖逆袭) 说明&#xff1a;本专栏持续更新中&#xff0c;目前专栏免费订阅&#xff0c;在转为付费专栏前订阅本专栏的&#xff0c;可以免费订阅付…

第二部分——长难句——第二章——复合句——第一节——名词性从句

首先复合句是什么呢&#xff1f; 多件事一样重要就叫并列句&#xff0c;如果不一样重要就叫并列句。 复合句主句从句 下面我们来看一个例句&#xff1a; 找到从句之后剩下来的不就是主句吗&#xff1f; 一&#xff0c;宾语从句 &#xff08;一&#xff09;宾语从句的含义 一…

字符串中的第一个唯一字符

题目&#xff1a;字符串中的第一个唯一字符 思路一&#xff1a;暴力查找&#xff0c;时间复杂度 思路一&#xff1a;开辟检查重复数组,以空间换时间&#xff0c;时间复杂度 这段代码的作用是在一个字符串 s 中找到第一个不重复&#xff08;只出现一次&#xff09;的字符&#…

kafka和rocketmq区别对比

Kafka和RocketMQ是目前非常流行的分布式消息系统&#xff0c;它们都能够高效地处理海量的消息数据。在本文中&#xff0c;我们将对Kafka和RocketMQ进行技术比较&#xff0c;分析它们在各方面的优缺点&#xff0c;以便读者在选择消息系统时能够做出更加明智的决策。 一、架构设计…

Java-volatile实现详解(从java到汇编)

在Java内存模型一节&#xff0c;除了synchronized外&#xff0c;我们还提到一个常用关键词----volatile&#xff0c;我们说过volatile保证了并发环境的可见性和顺序性&#xff0c;使用volatile修饰的变量&#xff0c;当然值发生改变时&#xff0c;可以同步到其他线程&#xff0…

【python】你的表情包缺不缺?我猜你缺了~来批量获取一波吧~

目录 前言环境使用:模块使用:如何去实现本次案例: <通用模板>代码展示尾语 &#x1f49d; 前言 嗨喽~大家好呀&#xff0c;这里是魔王呐 ❤ ~! 在我们的生活中&#xff0c;总少不了表情包来调剂 少了表情包就会少了很多趣味 今天就来教大家如何批量下载我们的表情包 环…

力扣刷题2023-04-29-1——题目:1431. 拥有最多糖果的孩子

题目&#xff1a; 给你一个数组 candies 和一个整数 extraCandies &#xff0c;其中 candies[i] 代表第 i 个孩子拥有的糖果数目。 对每一个孩子&#xff0c;检查是否存在一种方案&#xff0c;将额外的 extraCandies 个糖果分配给孩子们之后&#xff0c;此孩子有 最多 的糖果…

接触Word的编辑保护【简单版】

首先&#xff0c;我们遇到的情况是下图这样的&#xff1a; 点一下停止保护&#xff0c;是下图这样的&#xff1a; 开始解决&#xff1a; 第一种方式&#xff1a;另存为Word Xml 存好了&#xff0c; 打开如下图&#xff08;我用VS打开的&#xff0c;最好找个能够格式化代码的编…

秘密是如何被泄露的?自建文件分享神器HFS

近年来&#xff0c;随着互联网技术的飞速发展&#xff0c;我们对于互联网的依赖也日益增加。利用互联网这种高效便捷的工具&#xff0c;我们可以随时随地进行文件的传输、图片的分享以及与他人的交流。然而&#xff0c;看似安全的聊天窗口&#xff0c;其实背后是庞大的互联网算…

MybatisPlus动态表名

核心代码 mybatisplus的配置 Configuration public class MybatisPlusConfig {// 这里是存储需要动态变化的表&#xff0c;防止乱操作static List<String> tableList() {List<String> tables new ArrayList<>();//表名tables.add("user");return…

Flask框架的学习---01

1.工程搭建&#xff1a; 安装flask: pip3 install flask 终端运行&#xff1a;flask run 绑定IP地址和端口&#xff1a;Flask run -h 127.0.0.1 -p 8083 修改端口号生产环境&#xff1a;set FLASK_ENVproduction开发模式&#xff1a;set FLASK_ENVdevelopment 虽然 flask …

ChatGPT真的有那么牛吗?

ChatGPT真的有那么牛吗&#xff1f;ChatGPT真的有那么牛吗&#xff1f; 作为一款大型语言模型&#xff0c;ChatGPT确实具有很高的自然语言处理和生成能力&#xff0c;可以生成流畅、准确和有逻辑性的语言&#xff0c;而且能够理解和回答广泛的问题。 它是目前最先进和最强大的…