数据结构(初阶1)

news2024/11/15 15:29:16

文章目录

一、复杂度概念

二、时间复杂度

  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 )

四、常见复杂度对比

完结~~

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

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

相关文章

区间贪心

目录 1.贪心算法的思想 2.区间贪心算法常用的一些题目类型 1.选择最多不相交区间问题 P2970 [USACO09DEC] Selfish Grazing S 1.思路分析 2.上代码 2.区间选点问题 P1250 种树 1.题目 2.方法一 1.代码解释 3.方法二 3.区间合并问题 P2434 [SDOI2005] 区间 1. 思路…

容器docker

文章目录 前言一、docker1.1 为什么有docker1.2 docker架构1.3 docker 安装1.4 docker中央仓库1.5 docker 基本指令1.6 docker数据卷&#xff0c;挂载例&#xff1a;nginx 数据卷挂载例&#xff1a;mysql 本地持久化 1.7 镜像制作镜像结构dockerfile基础指令容器生成镜像 1.8 d…

JFlash读取和烧录加密stm32程序

JFlash读取和烧录加密stm32程序 安装后JFlash所在的目录&#xff1a;C:\Program Files\SEGGER\JLink 一、烧写加密程序 1、打开C:\Program Files\SEGGER\JLink目录&#xff0c;找到JFlash.exe,双击它&#xff0c;就可以打开该执行程序。见下图&#xff1a; 2、选择“Create …

Vue基础知识:Vue3.3出现的defineOptions,如何使用,解决了什么问题?

1.那么为什么会出现defineOptions? 原因说明&#xff1a; 有<script setup></script>语法糖应用之前&#xff0c;如果要定义 props&#xff0c;emits 可以轻而易举地添加一个与 setup 平级的属性。但是用了<script setup>后&#xff0c;就没法这么干了整个…

地理服务零成本:免费地图API合辑

在全球化和数字化不断推进的今天&#xff0c;地图已成为我们日常生活和工作中不可或缺的工具。无论是规划日常通勤、探索未知地域&#xff0c;还是进行地理数据分析&#xff0c;地图都发挥着至关重要的作用。它们不仅提供了地理信息的直观表示&#xff0c;还支持复杂的空间查询…

【U8+】登录U8时,选择账套登录窗口闪退。

【问题描述】 打开用友U8企业应用平台登录窗口&#xff0c; 输入账号和密码后&#xff0c;选择账套的时候闪退。 【解决方法】 方法一&#xff1a; 重装微软的silverlight&#xff0c;在U8soft\3rdprogram中有安装包。 注&#xff1a;不要自动更新此程序版本。关闭杀毒软件防火…

【线程安全】线程互斥的原理

文章目录 Linux线程互斥线程互斥相关概念互斥量mutex引出线程并发问题引出互斥锁、互斥量 互斥量的接口初始化互斥量销毁互斥量互斥量加锁和解锁使用互斥锁抢票 可重入和线程安全概念&#xff1a;常见线程不安全的情况常见线程安全的情况常见不可重入的情况常见可重入情况可重入…

jvm 05JVM - 对象的创建 ,oop模型,字符串常量池

01.JVM - 对象的创建 1、对象的创建的方式 Java语言中&#xff0c;对象创建的方式有六种&#xff1a; new关键字&#xff1a;最常见的形式、Xxx的静态方法、XxxBuilder、XxxFactory的静态方法。 Class类的newInstance()方法&#xff1a;通过反射的方式创建对象&#xff0c;调…

92. 反转链表 II (Swift 版本)

题目描述 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 分析 这是一个经典的链表问题&#xff0c;要求反转链表的部分节点。我们可以通过以下步骤实…

Codesys 连接 EtherCAT 总线伺服

本文内容是根据参考视频做的笔记&#xff1a; EtherCAT Master 控制&#xff1a;https://www.bilibili.com/video/BV1L14y1t7ks/EtherCAT Master Motion 控制&#xff1a;https://www.bilibili.com/video/BV16P411j71E/ EtherCAT 总线简单介绍 从站站号&#xff1a;如果使用扫…

【pytorch22】激活函数与GPU加速

激活函数 ReLu还是还是可能出现梯度弥散&#xff0c;因为x<0的时候&#xff0c;梯度还是可能小于0 leaky relu&#xff0c;在x<0的时候&#xff0c;梯度就不会为0&#xff0c;梯度不会不动 Relu函数在x0处是不连续的 一种更加光滑的曲线是SELU&#xff0c;是两个函数的…

ESXI6.7升级补丁报错VIB QLC_bootbank_qedrntv

1、报错如下图 2、原因 VMware在下方链接说的很清楚&#xff0c;报错原因为OEM提供的镜像与新版本补丁某些驱动不兼容&#xff1b; https://knowledge.broadcom.com/external/article?legacyId78487https://knowledge.broadcom.com/external/article?legacyId78487 3、解决 …

自动化立体仓库设计步骤:7步

导语 大家好&#xff0c;我是社长&#xff0c;老K。专注分享智能制造和智能仓储物流等内容。 完整版文件和更多学习资料&#xff0c;请球友到知识星球【智能仓储物流技术研习社】自行下载 这份文件是关于自动化立体仓库设计步骤的详细指南&#xff0c;其核心内容包括以下几个阶…

Git常用技能速成

文章目录 一.版本控制二.提交并推送代码三.提交推送代码 一.版本控制 接下来&#xff0c;我们就需要对我们的功能进行优化&#xff0c;但是需要说明的是&#xff0c;我们不仅仅要对上述提到的缓存进行优化&#xff0c;还需要对我们程序的各个方面进行优化。我们本章节主要是针…

mirthConnect 常用示例和语法整理

mirthConnect 常用示例和语法整理 1、jolt json常用语法 https://please.blog.csdn.net/article/details/140137463 2、常用方法 2.1 WinningDateUtils 所有的时间工具在WinningDateUtils里面 获取当前时间&#xff1a;var nowStrWinningDateUtils.getStandardNowStr()获取…

【C++】开源:格式化库fmt配置与使用

&#x1f60f;★,:.☆(&#xffe3;▽&#xffe3;)/$:.★ &#x1f60f; 这篇文章主要介绍格式化库fmt配置与使用。 无专精则不能成&#xff0c;无涉猎则不能通。——梁启超 欢迎来到我的博客&#xff0c;一起学习&#xff0c;共同进步。 喜欢的朋友可以关注一下&#xff0c;下…

Android 通知访问权限

问题背景 客户反馈手机扫描三方运动手表&#xff0c;下载app安装后&#xff0c;通知访问权限打不开。 点击提示“受限设置” “出于安全考虑&#xff0c;此设置目前不可用”。 问题分析 1、setting界面搜“授予通知访问权限”&#xff0c;此按钮灰色不可点击&#xff0c;点…

Linux系统下anaconda的安装与Pytorch环境的下载

首先&#xff0c;在命令行通过cd命令&#xff0c;进入用户文件夹 cd xxx/xxx/username进入anaconda官网https://repo.anaconda.com/archive/&#xff0c;寻找anaconda下载包资源&#xff0c;这里选择最新的anaconda下载包 Anaconda3-2024.06-1-Linux-x86_64.sh 在命令行执行安…

项目收获总结--Redis的知识收获

一、概述 最近几天公司项目开发上线完成&#xff0c;做个收获总结吧~ 今天记录Redis的收获和提升。 二、Redis异步队列 Redis做异步队列一般使用 list 结构作为队列&#xff0c;rpush 生产消息&#xff0c;lpop 消费消息。当 lpop 没有消息的时候&#xff0c;要适当sleep再…

土壤检测仪器:精确地检测土壤元素

在农业生产的广阔天地中&#xff0c;土壤检测仪器如同一把钥匙&#xff0c;打开了我们认识土壤、了解土壤元素的大门。这些看似平凡却功能强大的设备&#xff0c;能够精确地检测出土壤中的各种元素&#xff0c;为农业生产提供科学、准确的数据支持。 一、土壤检测仪器的重要性 …