当前位置: 首页 > news >正文

【初阶数据结构篇】时间(空间)复杂度

文章目录

    • 算法
    • 复杂度
      • 时间复杂度
        • 1. 定义
        • 2. 表示方法
        • 3. 常见时间复杂度
        • 4.案例计算分析
            • 冒泡排序
            • 二分查找
            • 斐波那契数列(递归法)
            • 斐波那契数列(迭代法)
      • 空间复杂度
      • 案例分析
            • 冒泡排序
            • 斐波那契数列(递归法)
            • 斐波那契数列(迭代法)
      • 时间复杂度对比

正文

算法

算法(Algorithm):就是定义良好的计算过程,他取⼀个或⼀组的值为输⼊,并产⽣出⼀个或⼀组值作为输出。简单来说算法就是⼀系列的计算步骤,⽤来将输⼊数据转化成输出结果。

​ 程序=数据结构+算法,一个好的程序需要有一个好的算法,那如何去衡量一种算法的好坏呢?这就需要我们计算算法的复杂度。

复杂度

​ 复杂度是计算机科学中的一个基础概念,它帮助我们理解和评估算法的效率,对于算法设计和优化至关重要。算法的复杂度通常分为时间和空间复杂度两个方面。

时间复杂度


1. 定义

​ 在计算机科学中,算法的时间复杂度是⼀个函数式T(N),它定量描述了该算法的运⾏时间。时间复杂度是衡量程序的时间效率,那么为什么不去计算程序的运⾏时间呢?

​ 1.因为程序运⾏时间和编译环境和运⾏机器的配置都有关系,⽐如同⼀个算法程序,⽤⼀个⽼编译器进⾏编译和新编译器编译,在同样机器下运⾏时间不同。

​ 2.同⼀个算法程序,⽤⼀个⽼低配置机器和新⾼配置机器,运⾏时间也不同。

​ 3.并且时间只能程序写好后测试,不能写程序前通过理论思想计算评估。


2. 表示方法

​ 如定义所示,时间复杂度是一个函数式T(N),T(N)通过表示程序的指令的执行次数来定量描述程序的运行时间。

​ 例如,T(N)=10,T(N)=2N+10,T(N)=N2等等等,都是描述一个程序指令的执行次数

​ 但是,设想一下,采用这样的表示方法,如果两种算法一种T(N)=N2,另一种T(N)=N2+100,当N越大时二者差距就可以忽略不计,如果我们仍然这样表示,不免缺乏简洁性和统一性。

大O渐进表示法

​ 在这基础上,我们联想数学中所学的等阶无穷大概念,数学中使用小o来表示高阶无穷小,而采用大O来表示等阶无穷大。具体的来说,对于函数T(N),当N趋于无穷时,我们能否找到这样一个函数f(N),使得 T ( N ) f ( N ) \frac{T(N)}{f(N)} f(N)T(N)为一个常数,答案是可以的。


3. 常见时间复杂度

下面列出了一些常见时间复杂度O(N)的表示法,即f(N)的常见形式。

  • 常数时间复杂度:O(1),表示算法的执行时间不随输入规模的增长而变化,是最理想的情况。
  • 对数时间复杂度:O(log n),通常出现在二分查找等分治算法中。
  • 线性时间复杂度:O(n),表示算法的执行时间与输入规模成正比。
  • 线性对数时间复杂度:O(n log n),通常出现在快速排序、归并排序等分治算法中。
  • 平方时间复杂度:O(n2),通常出现在嵌套循环的算法中。
  • 指数时间复杂度:O(2n),通常出现在递归算法中。
  • 多项式时间复杂度:O(nk),k可能是大于 2 的正整数,这意味着算法在大规模数据上的性能下降较快。

4.案例计算分析
冒泡排序
// 计算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;}
}
  • 最好情况,若数组有序

​ T(N) =N

  • 最差情况,若数组与要求顺序反序

​ T(N) = N ∗ ( N − 1 ) 2 \frac{N*(N-1)}{2} 2N(N1)

有n个元素,需要排n-1趟,第i趟需要排n-i次

即(n-1)+(n-2)+……+1,结果如上所示

我们发现上述第一种是最好情况,而第二种是最坏情况,俗话说的好“做最好的准备,做最坏的打算”,所以很合理的我们应该将最坏的情况计算结果当做时间复杂度,上述即T[N]=N2


二分查找
// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{assert(a);int begin = 0;int end = n - 1;while (begin < end){int mid = begin + ((end - begin) >> 1);if (a[mid] < x)begin = mid + 1;else if (a[mid] > x)end = mid;elsereturn mid;}return -1;
}
  • 最好情况

    O(1)

  • 最坏情况

    O(logN)

    一次对半筛选,当数据很多时筛选k次才找到,2k=N,对数函数增长规律一样,为了保持统一性,下标可以忽略,建议写法即为logN。


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

在这里插入图片描述

以此类推,即为O(2N)。

斐波那契数列(迭代法)
long long  Fib(size_t n)
{int a = 1;int b = 1;int c = 1;while (n > 2){c = a + b;a = b;b = c;n--;}return c;
}
  • 可见我们使用迭代的方法,将时间复杂度降为了O(N)。

空间复杂度

类比时间复杂度,相似内容不再过多赘述了

  • 空间复杂度也是⼀个数学表达式,是对⼀个算法在运⾏过程中因为算法的需要额外临时开辟的空间。

  • 空间复杂度不是程序占⽤了多少bytes的空间,因为常规情况每个对象⼤⼩差异不会很⼤,所以空间复杂度算的是变量的个数。

  • 注意:函数运⾏时所需要的栈空间(存储参数、局部变量、⼀些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运⾏时候显式申请的额外空间来确定!


案例分析

下面我们用具体案例来熟悉空间复杂度的计算

冒泡排序
// 计算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;}
}
  • 我们只申请了常数个变量,所以很显然空间复杂度为O(1)

斐波那契数列(递归法)
// 计算斐波那契递归Fib的时间复杂度?
long long Fib(size_t N)
{if (N < 3)return 1;return Fib(N - 1) + Fib(N - 2);
}
  • 空间复杂度为O(N)
  • 当我们输入N时,第一步递归将会沿着N-1,N-2一直到3递推,在n为3这个函数栈帧里调用2和1,并返回给3注意此时1和2的函数栈帧已经销毁,以此类推,返回一个销毁一个,程序在执行时同一时刻实际使用的空间不会超过n个(即往下递推的深度),只是每个n值相同函数栈帧在不同时刻执行了一次又一次的销毁创建过程,即在时间复杂度里面我们所讨论的调用次数的问题,但在空间复杂度中我们只关心使用的空间,故为O(N)。

斐波那契数列(迭代法)
long long  Fib(size_t n)
{int a = 1;int b = 1;int c = 1;while (n > 2){c = a + b;a = b;b = c;n--;}return c;
}
  • 这个也很显而易见了,为O(1)。

时间复杂度对比

下面是常见时间复杂度对比

在这里插入图片描述

在这里插入图片描述

http://www.lryc.cn/news/408275.html

相关文章:

  • C# 设计模式分类
  • 前端模块化CommonJS、AMD、CMD、ES6
  • 论文阅读:(DETR)End-to-End Object Detection with Transformers
  • react中路由跳转以及路由传参
  • C++ STL set_symmetric_difference
  • postman请求响应加解密
  • 数据集,批量更新分类数值OR批量删除分类行数据
  • 一款功能强大的视频编辑软件会声会影2023
  • 政安晨【零基础玩转各类开源AI项目】基于Ubuntu系统部署LivePortrait :通过缝合和重定向控制实现高效的肖像动画制作
  • 在Spring项目中使用Maven和BCrypt来实现修改密码功能
  • RedHat8安装Oracle19C
  • React系列面试题
  • C#:通用方法总结—第6集
  • Spark实时(一):StructuredStreaming 介绍
  • LangChain4j-RAG基础
  • git--本地仓库修改同步到远程仓库
  • 剑和沙盒 3 - 深度使用和解析Windows Sandbox
  • 深度学习loss
  • 编写一个Chrome插件,网页选择文字后,右键出现菜单“search with bing”,选择菜单后用bing搜索文字
  • 【算法】分割回文串
  • lua 游戏架构 之 游戏 AI (三)ai_attack
  • 大数据之Oracle同步Doris数据不一致问题
  • visual studio 问题总结
  • go-错误码的最佳实践
  • Python面试题:使用Matplotlib和Seaborn进行数据可视化
  • 模拟实现c++中的vector模版
  • uniapp安卓通过绝对路径获取文件
  • Known框架实战演练——进销存业务单据
  • 解决npm依赖树冲突的方法以及npm ERR! code ERESOLVE错误的解决方案
  • Spring Boot + Spring Batch + Quartz 整合定时批量任务