数据结构——算法的时间复杂度

news2024/9/24 13:25:06

🌇个人主页:_麦麦_

📚今日名言:生命中曾经有过的所有灿烂,都终究需要用寂寞来偿还。——《百年孤独》

目录

一、前言

二、正文

        1.算法效率

                1.1如何衡量一个算法的好坏

                1.2算法的复杂度

        2. 时间复杂度

                2.1时间复杂度的概念

                2.3 常见时间复杂度计算举例

三、结语


一、前言

        小伙伴们好呀,今天为大家带来的是算法的相关知识,主要围绕算法的效率时间复杂度并伴有一定的题目练习,希望能够为读者们带来一定的收获。

二、正文

        1.算法效率

                1.1如何衡量一个算法的好坏

        相信在座的小伙伴们一定见识了许多题目,也一定想出了相应的解决方案。不可否认的是,在面对同一道题目,同一个需求,不同的人也会写出不同的解决方案,那么到底如何如何评判这些解决方案的好坏呢?

        有的小伙伴可能会说这还不简单嘛,之间看谁的算法跑的快,也就是依据时间效率的高低来评判算法的好坏。不过在实际中,由于算法的运行环境不同,例如在不同的设备上同一算法的时间效率都可能是不一样的,更遑论是不同的算法了。显然,用时间效率来衡量一个算法的好坏是不可能的,那么有什么更好的方法呢?

                1.2算法的复杂度

         算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源。因此衡量一个算法的好坏主要是从时间空间两个维度来衡量的,即时间复杂度空间复杂度

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

        2. 时间复杂度

                2.1时间复杂度的概念

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

        即:找到某条基本语句与问题规模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\n", count);
}

         看过上面的代码,相信你们一定能够很轻易的算出Fun1执行的基本操作次数是一个关于N的函数:F(N)=N²+2N+10

●N=10          F(N)=130

●N=100        F(N)=10210

●N=1000      F(N)=1002010

注:实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,具体操作是怎样呢,接下来我们引入大O的渐进表达法

                2.2大O的渐进表示法

大O符号:适用于描述函数渐进行为的数学符号

推导大O阶方法:

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

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

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

        因此,在上面的例子中,采取大O的渐进表示法以后,Fun1的时间复杂度为:F(N²)

●N=10          F(N)=130

●N=100        F(N)=10210

●N=1000      F(N)=1002010

        通过上面我们会发现大O的渐进表示法去掉了哪些对结果影响不大的项,简洁明了的表示出了执行次数。不过随着算法的深入,我们会发现有些算法的时间复杂度并不是唯一确定的,而是存在着最好、平均和最坏情况: 

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

●平均情况:任意输入规模的期望运行次数

●最好情况:任意输入闺蜜的最小运行次数(下界)

        例如:在一个长度为N的数组中搜查一个数据x

●最坏情况:N次找到

●平均情况:N/2次找到

●最好情况:1次找到

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

         之所以以最坏情况来作为算法的时间复杂度,在这里其实是应用了信息学中的“预期管理”,就好比情人节那天,你要与你的男/女朋友约会,而约会时间可能是17点、18点、19点,那么相信大多数的人都会选择19点,如果刚刚好19点来就挺守时的嘛,如果早来,还可以提前准备,给他/她一些惊喜。相反,如果你将约定时间定位17点或者18点,一旦无法在约定时间前来,就是放鸽子了,可能会产生意料之外的各种特殊情况……

        因此,采取最坏情况作为时间复杂度的算法跑起来只有惊喜和预期,不可能比预期更坏

                2.3 常见时间复杂度计算举例

●实例1

// 计算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);
}

       通过计算,F(N)=2N+10,因此该算法的时间复杂度为O(N) 

●实例2

// 计算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);
}

        通过计算,在一般情况下,也就是M和N均为未知数的情况下,F(N)=M+N,所以O(N)=M+N 

注:如果题目中说M远大于N,那么O(N)=M

●实例3

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

        通过计算我们发现无论N取何值,F(N)始终等于一个常数也就是 ,根据 大O阶方法的一条,用1来代表所有常数,也就是该时间复杂度为O(1)

●实例4

// 计算strchr的时间复杂度
const char * strchr ( const char * str, int character );

        对库函数有过一定了解的同学一定知道“strchr”这个函数会依次对str指针+1,如果找到目的字符便会返回其指针,找不到便会返回空指针。而这里就会存在最好、最坏和平均情况,因此该函数的时间复杂度为O(N)

注:未指定未知数的情况下默认为N

●实例5

// 计算BubbleSort的时间复杂度
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;
         }
     }
 }
}

        最好情况:O(N)——无须排序

        最坏情况:O(N²)——全部交换

相信最坏情况的时间复杂度大家应该可以很轻易的想出来,但是最坏情况可能就有些复杂了。我们都知道冒泡排序共有两个流程,一个是趟数,一个是每趟交换的次数。那么时间复杂度就是总的交换次数。在看过博主之前推文对冒泡排序的小伙伴应该十分了解在冒泡排序最坏的情况下中,首趟交换的次数是最多的,为元素个数减,即N-1,之后虽趟数的增加,每趟交换的次数以1的速度递减。那么总的趟数计算的话其实就是一个高中很简单的等差数列求和,即(首项+尾项)*项数/2,代入N-1,可得F(N)=N*(N-1)/2,所以O(N)=N²。

 ●实例6

// 计算BinarySearch的时间复杂度——二分查找
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;
}

        二分查找又称折半查找法,是一种通过中间值与目标值进行比较,进而对数值进行筛选,循环往复来查找目标值的方法。

        最好情况下,也就是第一次就找到目标值,此时时间复杂度为O(1)

        最坏情况下,就是将数组不断二分直至长度为1为止,此时可能找到目标值也可能没有目标值。因此F(N)=log以2为底的对数,大部分情况下简写为logN,所以O(N)=logN。

●实例7

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

    return Fac(N - 1) * N;
}

  

         有时候,我们可能在脑海中无法形象的,一下子的得出时间复杂度,这时候就可以通过画图来帮助自己理清思路,进而得出时间复杂度。通过上图,我们可以发现在最坏的情况下基本操作进行了N次,时间复杂度为O(N)

● 实例8

// 计算斐波那契递归Fib的时间复杂度
long long Fib(size_t N)
{
 if(N < 3)
 return 1;
 
 return Fib(N-1) + Fib(N-2);
}

 

        我们发现这其实是一个等比数列求和,虽然右边有一些数提前递归结束,但是随着N的增大,是可以忽略不计的。因此该时间复杂度为O(2^N) 

三、结语

         到此为止,关于算法的时间复杂度的学习就告一段落了。

         关注我 _麦麦_分享更多干货:_麦麦_的博客_CSDN博客-领域博主
         大家的「关注❤️ + 点赞👍 + 收藏⭐」就是我创作的最大动力!谢谢大家的支持,我们下期见!

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

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

相关文章

元胞自动机

文章目录前言文献阅读摘要主要贡献方法框架实验结论元胞自动机元胞自动机是什么&#xff1f;构成及规则案例及代码实现总结前言 This week,the paper proposes a Multi-directional Temporal Convolutional Artificial Neural Network (MTCAN) model to impute and forecast P…

部署dapr的辛酸历程

前言dapr大概的了解&#xff0c;个人理解他就是一个分布式服务的管理&#xff0c;把微服务常用的组件(缓存&#xff0c;消息中间件、分布式锁、安全id4等)和监控以及服务注册、发现等等一系列功能以一个很抽象的方式管理起来。可能我们部署微服务用consul、ocelot、polly套件、…

DDD单根 聚合根 实体 值对象

前言2004年Eric Evans 发表Domain-Driven Design –Tackling Complexity in the Heart of Software &#xff08;领域驱动设计&#xff09;&#xff0c;简称Evans DDD。快二十年的时间&#xff0c;领域驱动设计在不断地发展&#xff0c;后微服务时代强调的东西&#xff0c;在国…

Nginx网站服务及优化

Nginx网站服务及优化一、简介1、Nginx概述2、Nginx和Apache的优缺点比较3、Nginx和Apache最核心的区别二、Linux中的I/O三、Nginx编译安装详细1、关闭防火墙、安装依赖关系2、新建用户nginx便于管理3、将压缩包传入到/opt目录下&#xff0c;编译安装4、做软连接并启动nginx5、创…

软件测试简历个人技能和项目经验怎么写?(附项目资料)

目录 前言 个人技能 项目实战经验 项目名称&#xff1a;苏州银行项目&#xff08;webapp&#xff09; 项目描述&#xff1a; 项目名称&#xff1a;中国平安项目&#xff08;webapp&#xff09; 项目描述&#xff1a; 项目名称&#xff1a;苏宁易购项目&#xff08;webapp&a…

软件体系结构(期末复习)

文章目录软件体系结构软件体系结构概论软件体系结构建模软件体系结构风格统一建模语言基于体系结构的软件开发软件体系结构 软件体系结构概论 软件危机是指计算机软件的开发和维护过程中遇到的一系列严重问题。 软件危机的表现: 软件危机的原因: 软件工程的基本要素&#xf…

轻松上手nacos使用

三步上手nacos使用1.为什么使用nacos?2.如何使用nacos1.为什么使用nacos? 1.服务发现中心。 微服务将自身注册至 Nacos&#xff0c;网关从 Nacos 获取微服务列表。 2.配置中心。 微服务众多&#xff0c;它们的配置信息也非常复杂&#xff0c;为了提高系统的可维护性&#xf…

每天一个linux命令---awk

awk命令 1. 简介 awk是一种处理文本文件的语言&#xff0c;是一个强大的文本分析工具&#xff0c;grep、sed、awk并称为shell中文本处理的三剑客。 AWK 是一种处理文本文件的语言&#xff0c;是一个强大的文本分析工具。 之所以叫 AWK 是因为其取了三位创始人 Alfred Aho&am…

1.OCR--文本检测算法FCENet

文章目录1.简介2.主要工作2.1 傅里叶轮廓嵌入(Fourier Contour Embedding)2.2 FCE模型3.代码实现参考资料欢迎访问个人网络日志&#x1f339;&#x1f339;知行空间&#x1f339;&#x1f339; 论文:Fourier Contour Embedding for Arbitrary-Shaped Text Detection 1.简介 这…

设计模式之中介模式与解释器模式详解和应用

目录1 中介模式详解1.1 中介模式的定义1.1.1 中介者模式在生活场景中应用1.1.2 中介者模式的使用场景1.2 中介模式的通用实现1.2.1 类图设计1.2.2 代码实现1.3 中介模式应用案例之聊天室1.3.1 类图设计1.3.2 代码实现1.4 中介者模式在源码中应用1.4.1 jdk中Timer类1.5 中介者模…

logd守护进程

logd守护进程1、adb logcat命令2、logd守护进程启动2.1 logd文件目录2.2 main方法启动3、LogBuffer缓存大小3.1 缓存大小优先级设置3.2 缓存大小相关代码位置android12-release1、adb logcat命令 命令功能adb bugreport > bugreport.txtbugreport 日志adb shell dmesg >…

kafka-11-kafka的监控工具和常用配置参数

kafka官方文档 参考Kafka三款监控工具比较 1 查看kafka的版本 进入kafka所在目录&#xff0c;通过查看libs目录下的jar包。 2.11是scala的版本&#xff0c;2.0.0是kafka的版本。 测试环境 #systemctl start zookeeper #systemctl start kafkka 2 kafka的常用配置 Kafka使用…

SpringBoot2.x实战专题——SpringBoot2 多配置文件【开发环境、测试环境、生产环境】

SpringBoot2.x实战专题——SpringBoot2 多配置文件【开发环境、测试环境、生产环境】 目录SpringBoot2.x实战专题——SpringBoot2 多配置文件【开发环境、测试环境、生产环境】一、创建一个SpringBoot项目二、修改pom.xml中SpringBoot的版本三、配置文件3.1 application-dev.ym…

2.TCP/UDP什么时候选择,HTTP,使用TCP/UDP的协议有哪些,TCP三次握手四次挥手大概流程,为什么要三次握手.

文章目录1.什么时候选择 TCP,什么时候选 UDP?2. HTTP 基于 TCP 还是 UDP&#xff1f;3.使用 TCP 的协议有哪些?使用 UDP 的协议有哪些?4.TCP 三次握手和四次挥手&#xff08;非常重要、传输层&#xff09;5.为什么要三次握手?1.什么时候选择 TCP,什么时候选 UDP? UDP 一般…

Spring Cloud Nacos实战(五)- 命名空间分组和DataID三者关系

Nacos命名空间分组和DataID三者关系 名词解释 命名空间&#xff08;Namespace&#xff09; ​ 用于进行租户粒度的配置隔离。不同的命名空间下&#xff0c;可以存在相同的 Group 或 Data ID 的配置。Namespace 的常用场景之一是不同环境的配置的区分隔离&#xff0c;例如开发…

Kubernetes一 Kubernetes之入门

二 Kubernetes介绍 1.1 应用部署方式演变 在部署应用程序的方式上&#xff0c;主要经历了三个时代&#xff1a; 传统部署&#xff1a;互联网早期&#xff0c;会直接将应用程序部署在物理机上 优点&#xff1a;简单&#xff0c;不需要其它技术的参与 缺点&#xff1a;不能为应…

机器学习实战教程(六):决策树

决策树 决策树是什么&#xff1f;决策树(decision tree)是一种基本的分类与回归方法。举个通俗易懂的例子&#xff0c;如下图所示的流程图就是一个决策树&#xff0c;长方形代表判断模块(decision block)&#xff0c;椭圆形成代表终止模块(terminating block)&#xff0c;表示…

SpringBoot实战——个人博客项目

目录 一、项目简介 二、项目整体架构 数据库模块 后端模块 前端模块 三、项目具体展示 四、项目的具体实现 1、一些准备工作 &#x1f34e;数据库、数据表的创建 &#x1f34e;设置数据库和MyBatis的配置 &#x1f34e;将前端项目引入到当前项目中 2、登录注册模块 &…

CV学习笔记-Inception

CV学习笔记-Inception 目录 文章目录CV学习笔记-Inception目录1. 常见的卷积神经网络2. Inception(1) Inception提出背景(2) Inception module 核心思想3. Inception的历史版本(1) InceptionV1-GoogleNet(2) InceptionV2(3) InceptionV3(4) Inception V44. Inception模型的特点…

一起学 pixijs(4):如何绘制文字md

大家好&#xff0c;我是前端西瓜哥&#xff0c;今天我们来学 pixijs 如何绘制文字。pixijs 版本为 7.1.2。 使用原生的 WebGL 来绘制文字是非常繁琐的&#xff0c;pixijs 对此进行了高层级的封装&#xff0c;提供了 Text 类和 BitMapText 类来绘制文字。 Text 最基本的写法&…