数据结构(初阶1.复杂度)

文章目录

一、复杂度概念

二、时间复杂度

  2.1 大O的渐进表示法

    2.2 时间复杂度计算示例

    2.2.1. // 计算Func2的时间复杂度?

    2.2.2.// 计算Func3的时间复杂度?

    2.2.3.// 计算Func4的时间复杂度?

    2.2.4.// 计算strchr的时间复杂度?

     💡 总结

    2.2.5.// 计算BubbleSort (冒泡排序) 的时间复杂度?

    2.2.6.// 计算func5的时间复杂度?

    2.2.7.// 计算阶乘递归Fac的时间复杂度?

三、空间复杂度

  3.1 空间复杂度计算示例

    3.1.1// 计算BubbleSort的空间复杂度?

    3.1.2// 计算阶乘递归Fac的空间复杂度?

四、常见复杂度对比


一、复杂度概念

  • 算法在编写成可执⾏程序后,运⾏时需要耗费时间资源和空间(内存)资源 。因此衡量⼀个算法的好坏,⼀般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。 时间复杂度主要衡量⼀个算法的运⾏快慢,⽽空间复杂度主要衡量⼀个算法运⾏所需要的额外空间。在计算 机发展的早期,计算机的存储容量很⼩。所以对空间复杂度很是在乎。但是经过计算机⾏业的迅速发展,计算机的存储容量已经达到了很⾼的程度。所以我们如今已经不需要再特别关注⼀个算法的空间复杂度。

二、时间复杂度

定义:在计算机科学中,算法的时间复杂度是⼀个函数式T(N),它定量描述了该算法的运⾏时                   间,时间复杂度是衡量程序的时间效率,那么为什么不去计算程序的运⾏时间呢?
  1. 因为程序运⾏时间和编译环境和运⾏机器的配置都有关系,⽐如同⼀个算法程序,⽤⼀个⽼编译器进⾏编译和新编译器编译,在同样机器下运⾏时间不同。
  2. 同⼀个算法程序,⽤⼀个⽼低配置机器和新⾼配置机器,运⾏时间也不同。
  3. 并且时间只能程序写好后测试,不能写程序前通过理论思想计算评估。
那么算法的时间复杂度是⼀个函数式T(N)到底是什么呢?这个T(N)函数式计算了程序的执⾏次数。通过c语⾔编译链接章节学习,我们知道算法程序被编译后⽣成⼆进制指令,程序运⾏,就是cpu执⾏这些编译好的指令。那么我们通过程序代码或者理论思想计算出程序的执⾏次数的函数式T(N),
假设每句指令执⾏时间基本⼀样(实际中有差别,但是微乎其微),那么执⾏次数和运⾏时间就是等⽐正相关,这样也脱离了具体的编译运⾏环境。执⾏次数就可以代表程序时间效率的优劣。⽐如解决⼀个问题的算法a程序T(N) = N,算法b程序T(N) = N^2,那么算法a的效率⼀定优于算法b。

看下面代码 

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;
    }
}
  • 程序时间效率:每条语句运行时间(通过编译环境或者运行环境决定的,存在不确定性)  *  运                             行次数
  • Func1 执⾏的基本操作次数:
             T ( N ) = N 2 + 2 ∗ N + 10
N = 10       N2 = 100    2*N = 20       T(N) = 130
N = 100     N2 = 100    2*N = 200     T(N) = 10210
N = 1000   N2 = 100    2*N = 2000   T(N) = 1002010
通过对N取值分析,对结果影响最⼤的⼀项是 N 2.
实际中我们计算时间复杂度时,计算的也不是程序的精确的执⾏次数,精确执⾏次数计算起来还是很⿇烦的(不同的⼀句程序代码,编译出的指令条数都是不⼀样的),计算出精确的执⾏次数意义也不⼤,因为我么计算时间复杂度只是想⽐较算法程序的增⻓量级,也就是当N不断变⼤时T(N)的差别,上⾯我们已经看到了当N不断变⼤时常数和低阶项对结果的影响很⼩,所以我们只需要计算程序能代表增⻓量级的⼤概执⾏次数,复杂度的表⽰通常使⽤⼤O的渐进表⽰法。

2.1 大O的渐进表示法

⼤O符号(Big O notation):是⽤于描述函数渐进⾏为的数学符号
💡 推导⼤O阶规则
  1.  时间复杂度函数式T(N)中,只保留最⾼阶项,去掉那些低阶项,因为当N不断变⼤时,低阶项对结果影响越来越⼩,当N⽆穷⼤时,就可以忽略不计了。
  2.  如果最⾼阶项存在且不是1,则去除这个项⽬的常数系数,因为当N不断变⼤,这个系数对结果影响越来越⼩,当N⽆穷⼤时,就可以忽略不计了。
  3. T(N) 中如果没有 N 相关的项⽬,只有常数项,⽤常数 1 取代所有加法常数。
    通过以上⽅法,可以得到 Func1 的时间复杂度为: O ( N 2 )
2.2 时间复杂度计算示例
2.2.1. // 计算Func2的时间复杂度?
void Func2(int N)
{
    int count = 0;
    for (int k = 0; k < 2 * N ; ++ k)
    {
    ++count;                                  2N
    }
    int M = 10;
    while (M--)
    {
    ++count;                                  10
    }
    printf("%d\n", count);
}
  •  Func2执⾏的基本操作次数: F (N) = 2N + 10
  • 根据推导规则第3条得出,Func2的时间复杂度为: O(N)

2.2.2.// 计算Func3的时间复杂度?
void Func3(int N, int M)
{
    int count = 0;
    for (int k = 0; k < M; ++ k)
    {
    ++count;                                      M
    }
    for (int k = 0; k < N ; ++k)
    {
    ++count;                                      N
    }
    printf("%d\n", count);
}
  • Func3执⾏的基本操作次数: F ( N ) = M + N
  • 因此:Func2的时间复杂度为: O ( N )
注意:当  M>>N , O(M);
            当  M>>N , O(M);   

           当  M == N , O(M+N);

2.2.3.// 计算Func4的时间复杂度?
void Func4(int N)
{
    int count = 0;
    for (int k = 0; k < 100; ++ k)
    {
    ++count;
    }
    printf("%d\n", count);
}
  • Func4执⾏的基本操作次数:F (N) = 100
  • 根据推导规则第1条得出Func2的时间复杂度为: O (1)
注意:这里的 1 不是运行一次,而是代表常数。

2.2.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;
}

T(N)取决于查找的位置

strchr执⾏的基本操作次数:
1)若要查找的字符在字符串第⼀个位置,则:         F ( N ) = 1
2)若要查找的字符在字符串最后的⼀个位置,则: F ( N ) = N
3)若要查找的字符在字符串中间位置,则:            F ( N ) = N /2
因此:strchr的时间复杂度分为:
最好情况: O (1)
最坏情况: O ( N )
平均情况: O ( N/2) --> O ( N )
💡 总结
通过上⾯我们会发现,有些算法的时间复杂度存在最好、平均和最坏情况。
最坏情况:任意输⼊规模的最⼤运⾏次数(上界)
平均情况:任意输⼊规模的期望运⾏次数
最好情况:任意输⼊规模的最⼩运⾏次数(下界)
⼤O的渐进表⽰法在实际中⼀般情况关注的是算法的上界,也就是最坏运⾏情况。
2.2.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;
    }
}

外层循环次数 :    1       2       3       ......    end

内层循环次数 :    N     N-1   N-2     ......     0

BubbleSort执⾏的基本操作次数:
1)若数组有序,则:              F ( N ) = N
2)若数组有序且为降序,则: F ( N) = 1 + 2 + 3+...+ N = N ∗ ( N + 1)/2 -->N2
      因此:BubbleSort的时间复杂度取最差情况为: O ( N 2 )
2.2.6.// 计算func5的时间复杂度?
void func5(int n)
{
    int cnt = 1;
    while (cnt < n)
    {
    cnt *= 2;
    }
}
当 cnt = 2时,      执⾏次数为1
当 cnt = 4时,      执⾏次数为2
当 cnt = 16时,    执⾏次数为4
若要使该公式不成立,即跳出 while 循环,此时 x 最小为 4
假设执⾏次数为 x ,则 2 x = n
因此执⾏次数: x = log n
因此:func5的时间复杂度取最差情况为: O (log 2 n )
(注意课件中和书籍中 log 2 n log n lg n 的表⽰
当n接近⽆穷⼤时,底数的⼤⼩对结果影响不⼤。因此,⼀般情况下不管底数是多少都可以省略不
写,即可以表⽰为 log n
不同书籍的表⽰⽅式不同,以上写法差别不⼤,我们建议使⽤ log n)
2.2.7.// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{
    if(0 == N)
    return 1;
    return Fac(N-1)*N;
}

                     递归过程: Fac(n) ->  Fac(n-1) ->  Fac(n-2) -> ...  Fac(0) 

单次递归的时间复杂度: O(1)           O(1)            O(1)           ...   O(1)

递归的次数为N;

时间复杂度:单次递归的时间复杂度 * 递归次数:O(1)  * N = O(N)

阶乘递归的时间复杂度为: O (N )

三、空间复杂度

空间复杂度也是⼀个数学表达式,是对⼀个算法在运⾏过程中因为算法的需要额外临时开辟的空间。
空间复杂度不是程序占⽤了多少bytes的空间,因为常规情况每个对象⼤⼩差异不会很⼤,所以空间复杂度算的是变量的个数。
空间复杂度计算规则基本跟实践复杂度类似,也使⽤⼤O渐进表⽰法。
注意:函数运⾏时所需要的栈空间(存储参数、局部变量、⼀些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运⾏时候显式申请的额外空间来确定

3.1 空间复杂度计算示例

3.1.1// 计算BubbleSort的空间复杂度?

在 2.2.5 中我们计算了 BubbleSort 的时间复杂度,那么空间复杂度又是怎么计算呢?

void BubbleSort(int* a, int n)
{
    assert(a);  
    for (size_t end = n; end > 0; --end)  //  (size_t end = n)   申请一次
    {    
    int exchange = 0;                     //  (int exchange = 0) 申请一次
        for (size_t i = 1; i < end; ++i)  //  (size_t i)         申请一次  
        {
            if (a[i-1] > a[i])
            {
                Swap(&a[i-1], &a[i]);
                exchange = 1;
            }
        }
    if (exchange == 0)   //数组有序
    break;
    }
}
函数栈帧在编译期间已经确定好了, 只需要关注函数在运⾏时额外申请的空间。
BubbleSort额外申请的空间有 exchange、  size_t end  、size_t i 等 有限个局部变量,使⽤了常数个额外空间,因此空间复杂度为 O (1)。
3.1.2// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{
    if(0 == N)
    return 1;
    return Fac(N-1)*N;
}
Fac递归调⽤了N次,额外开辟了N个函数栈帧,每个栈帧使⽤了常数个空间
因此空间复杂度为: O ( N )

四、常见复杂度对比

完结~~

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

智能车载防窒息系统设计

摘要 随着汽车行业的快速发展&#xff0c;车辆安全问题越来越受到人们的关注。其中&#xff0c;车载防窒息系统是一项重要的安全设备。本论文基于STM32单片机&#xff0c;设计了一种智能车载防窒息系统。该系统主要包括氧气浓度检测模块、温湿度检测模块、声音检测模块、光线检…

URPF简介

定义 URPF&#xff08;Unicast Reverse Path Forwarding&#xff09;是单播逆向路径转发的简称&#xff0c;其主要功能是防止基于源IP地址欺骗的网络攻击行为。 目的 拒绝服务DoS&#xff08;Denial of Service&#xff09;攻击是一种阻止连接服务的网络攻击。DoS的攻击方式…

QT开发积累——qt中的注释和多行注释的几种方式,函数方法注释生成

目录 引出qt中的注释和多行注释方法的注释生成 总结日积月累&#xff0c;开发集锦方法参数加const和不加const的区别方法加static和不加static的区别Qt遍历list提高效率显示函数的调用使用&与不使用&qt方法的参数中使用&与不使用&除法的一个坑 项目创建相关新建…

交通气象站:保障道路安全的智慧之眼

随着社会的快速发展&#xff0c;交通运输日益繁忙&#xff0c;道路安全成为公众关注的焦点。在这个背景下&#xff0c;交通气象站作为保障道路安全的重要设施&#xff0c;正发挥着越来越重要的作用。它们不仅为交通管理部门提供及时、准确的气象信息&#xff0c;也为广大驾驶员…

Conformal low power-2.电源感知等效性检查

电源感知等效性检查 ■ 第24页&#xff1a;电源感知等效性检查概述 ■ 第24页&#xff1a;启动低功耗&#xff08;等效性检查&#xff09;软件 ■ 第25页&#xff1a;电源感知等效性检查流程 ■ 第28页&#xff1a;电源感知等效性检查示例Do文件 电源感知等效性检查概述…

【网络安全科普】网络安全指南请查收

随着社会信息化深入发展&#xff0c;互联网对人类文明进步奖发挥更大的促进作用。但与此同时&#xff0c;互联网领域的问题也日益凸显。网络犯罪、网络监听、网络攻击等是又发生&#xff0c;网络安全与每个人都息息相关&#xff0c;下面&#xff0c;一起来了解网络安全知识吧。…

从零到一:打造你的专属AI聊天机器人之旅

在这个智能技术日新月异的时代&#xff0c;AI聊天机器人已成为我们日常生活中不可或缺的伙伴&#xff0c;从客服咨询到情感交流&#xff0c;它们以独特的魅力融入了我们的每一个角落。你是否也曾梦想过亲手创造一个能够理解你、陪伴你的AI聊天机器人呢&#xff1f;今天&#xf…

After Detailer让图像自动修复

After Detailer&#xff08;简称adetailer&#xff09;是一个Stable Diffusion的自动Web-UI扩展&#xff0c;它能够自动化修复图像中的不完整部分&#xff0c;例如模糊的人脸等常见问题。在这篇文章中&#xff0c;你将了解它的工作原理、如何使用它&#xff0c;以及一些常见的使…

CSDN回顾与前行:我的创作之旅——2048天的技术成长与感悟

CSDN回顾与前行&#xff1a;我的创作之旅——2048天的技术成长与感悟 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 前言 时光荏苒&#xff0c;岁月如梭。转眼间&#xff0c;从我在CSDN上写下第一篇技术博客《2-6 带头结点的链式表操作集…

无线网的ip地址固定吗

在数字化日益普及的今天&#xff0c;无线网络已成为我们生活与工作中不可或缺的一部分。然而&#xff0c;对于许多非专业用户来说&#xff0c;无线网络背后的技术细节仍然充满了神秘感。其中&#xff0c;一个常见的问题是&#xff1a;无线网的IP地址是固定的吗&#xff1f;本文…

WebKit简介及其神秘的工作流程

在信息时代的巨浪中&#xff0c;互联网已经深深地渗透到了我们生活的每一个角落。作为连接我们与这个庞大网络世界的桥梁&#xff0c;网页浏览器无疑成为了我们生活中不可或缺的一部分。而在这些浏览器的背后&#xff0c;往往隐藏着一些强大而神秘的引擎&#xff0c;它们为浏览…

【源码+文档+调试讲解】沙县小吃点餐系统

摘 要 随着社会的发展&#xff0c;社会的各行各业都在利用信息化时代的优势。计算机的优势和普及使得各种信息系统的开发成为必需。 沙县小吃点餐系统&#xff0c;主要的模块包括实现管理员&#xff1b;个人中心、用户管理、小吃信息管理、门店信息管理、预约信息管理、系统管…

Windows 网络重置及重置网络可能出现的问题( WIFI 没有了 / WLAN 图标消失)

netsh int ip reset 命令是用于重置 Windows 操作系统中的网络设置和配置的命令。 在网络故障排除、修复网络连接问题以及清除可能存在的网络配置冲突时非常有用。 命令详解&#xff1a; netsh: 用于配置各种网络设置 int: 用于管理网络接口 ip: 用于管理网络接口的 IP 配…

酒库温度看板软件设计

摘要 随着酒类行业的发展&#xff0c;酒库的管理变得越来越重要。酒库是存放酒类产品的地方&#xff0c;其温度对酒类产品的质量和口感有着至关重要的影响。因此&#xff0c;监控和控制酒库温度是酒库管理的重要环节。 本论文针对酒库温度监测与管理的需求&#xff0c;设计了一…

GeoServer property 表达式注入代码执行漏洞(CVE-2024-36401)

GeoServer property 表达式注入代码执行漏洞(CVE-2024-36401) 1.漏洞描述 GeoServer 是一个开源的服务器软件&#xff0c;使用 Java 编写&#xff0c;主要功能是允许用户共享和编辑地理空间数据。它在设计时就考虑到了互操作性&#xff0c;支持使用开放标准来发布多种主流格式…

从数据仓库到数据湖(下):热门的数据湖开源框架

文章目录 一、前言二、Delta Lake三、Apache Hudi四、Apache Iceberg五、Apache Paimon六、对比七、笔者观点八、总结八、参考资料 一、前言 在上一篇从数据仓库到数据湖(上)&#xff1a;数据湖导论文章中&#xff0c;我们简单讲述了数据湖的起源、使用原因及其本质。本篇文章…

强化学习总结(有具体代码实现)

文章目录 第一部分 强化学习基础第1章 强化学习概述1.1 强化学习概念1.2 强化学习的环境1.3 强化学习的目标1.4 强化学习的数据 第2章 多臂老虎机问题&#xff08;MAB问题&#xff09;2.1 问题描述2.1.1 问题定义2.1.2 形式化描述2.1.3 累积懊悔2.1.4 估计期望奖励 2.2 解决方法…

Java泛型的定义与运用

泛型 泛型的作用从使用层面上来说是统一数据类型&#xff0c;防止将来的数据转换异常。从定义层面上来说&#xff0c;定义带泛型的类&#xff0c;方法等&#xff0c;将来使用的时候给泛型确定什么类型&#xff0c;泛型就会变成什么类型&#xff0c;凡是涉及到泛型的都会变成确…

【Mark笔记】基于Centos7.7更改SSH端口重启服务报错

0x0 场景描述 RT&#xff0c;更改默认端口22为2276后直接重启服务报错&#xff1a; 查看报错内容&#xff0c;如下&#xff1a; 0x1 相关操作 关闭selinux &#xff08;未重启&#xff09;本地防火墙端口放行tcp 2276端口更改回22端口服务可以正常启动sshd -t 检查配置并未…

【086】基于Springboot+vue实现的图书商城购物系统

系统介绍 视频演示 基于Springbootvue实现的图书商城购物系统采用前后端分离的架构方式&#xff0c;系统分为管理员、用户两个角色&#xff0c;实现了用户注册与登录、用户管理、书籍分类管理、书籍管理、轮播图管理、资讯管理、订单及发货管理等功能。 技术选型 开发工具&…