时空复杂度详解

简介: 我们写出来了一个算法,但是这个算法怎么样呢?

算法的效率

我们写出来了一个算法,但是这个算法怎么样呢?


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

有一些的算法代码很简洁,但是简洁能够说明算法的好坏吗?

答案是不能的。


算法的复杂度

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

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


时间复杂度

概念

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


总结一下:


找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。


大O的渐进表示法

大O符号(Big O notation):是用于描述函数渐进行为的数学符号。


推到大O阶的方法:


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

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

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

通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。

另外有些算法的时间复杂度存在最好、平均和最坏情况:

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

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

最好情况:任意输入规模的最小运行次数(下界)

在实际中一般情况关注的是算法的最坏运行情况。


实战

有了上面的基础,我们来几个例子试一下:

例一

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);
}

我们可以计算它的执行次数是:2N+10

那么它的时间复杂度用大O的渐进表示法就是:O(N)


例二

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

这是一个二分查找的算法,我们光看代码是很难看出它程序执行次数的,所我们大部分情况下都要跟根据算法的思想来计算时间复杂度,二分查找就是每一次查找都能去掉一半的元素,最坏的情况就是要查找的元素在边界或者不存在,所以最坏的情况下就要查找logN次,这里底数一般不写都默认为2。

所以它的时间复杂度就是O(logN)。


例三

long long Fac(size_t N)
{
  if(0 == N)
    return 1;
  return Fac(N-1)*N;
}

这里可以发现递归了N次,每一次递归都要执行一次,所以就是执行了N次。

所以它的时间复杂度就是:O(N)。


空间复杂度

概念

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度 。空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。


注意


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


实战

例一

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)。


例二

long long* Fibonacci(size_t n)
{
  if (n == 0)
    return NULL;
  long long* fibArray = (long long*)malloc((n + 1) * sizeof(long long));
  fibArray[0] = 0;
  fibArray[1] = 1;
  for (int i = 2; i <= n; ++i)
  {
    fibArray[i] = fibArray[i - 1] + fibArray[i - 2];
  }
  return fibArray;
}

这是一个非递归的计算前N个斐波那契数列我们可以看到malloc开辟了n+1个空间。

所以它的时间复杂度就是:O(N)。


常见复杂度的对比

一般算法常见的复杂度如下:

c5f9c936cce0450d957cebdf9f829795.png

a57eb55c93834dc380a6e13f340cc4a9.png

今天的分享就到这里,感谢大家的关注和支持!

相关文章
|
存储 安全 搜索推荐
虚拟云桌面中RDS、VDI技术比较
下面,就来看看两种主流的云桌面。 1、RDS(Remote Desktop Services,远程桌面服务),俗称共享云桌面 2、VDI(Virtual Desktop Infrastucture,虚拟桌面架构),俗称虚拟云桌面
3137 1
vscode ctrl+/ 注释快捷键失效
首次安装vscode 不知道为何会快捷键失效,首先想到的就是键位冲突! 于是解决了。
5858 0
vscode ctrl+/ 注释快捷键失效
|
8月前
|
Java API 调度
SpringBoot整合XXL-JOB【01】- 初识XXL-JOB
XXL-JOB 是一个分布式任务调度平台,设计目标为开发迅速、学习简单、轻量级、易扩展。它解决了分布式环境下定时任务重复执行的问题,无需额外加锁,降低了维护成本。XXL-JOB 由调度中心和执行器两部分组成,前者管理任务,后者执行具体逻辑,使代码结构更清晰。适用于多机部署场景,支持统一管理任务的启停和频率调整。
1045 8
SpringBoot整合XXL-JOB【01】- 初识XXL-JOB
|
存储 前端开发 开发工具
前端常用的git操作
【8月更文挑战第24天】前端常用的git操作
114 1
|
10月前
|
机器学习/深度学习 弹性计算 编解码
阿里云服务器c7/c8a/c8y/c8i/g7/g8a/g8y/g8i/r7/r8a/r8y/r8i实例区别及选择参考
在阿里云目前的活动中,除了特价的轻量应用服务器和经济型e及通用算力型u1实例之外,属于计算型实例的实例有计算型c7/c8a/c8y/c8i,属于通用型实例的有通用型g7/g8a/g8y/g8i,属于内存型实例的有内存型r7/r8a/r8y/r8i。本文将详细介绍阿里云服务器中的c7、c8a、c8y、c8i、g7、g8a、g8y、g8i、r7、r8a、r8y、r8i等实例规格的性能、适用场景及选择参考,帮助用户更好地选择合适的云服务器实例。
|
11月前
|
JavaScript 搜索推荐 数据处理
Vue Router 导航钩子
【10月更文挑战第17天】需要注意的是,过度使用导航钩子可能会导致代码复杂度增加,因此需要合理规划和使用。此外,不同的钩子在不同的场景下具有不同的作用,需要深入理解其特点和用法,才能更好地发挥它们的价值。可以在实际项目中结合具体需求,充分利用这些导航钩子来打造更加智能、灵活的路由导航机制。
120 1
|
数据库
Jumpserver——如何替换多因子认证
Jumpserver——如何替换多因子认证
215 0
|
机器学习/深度学习 测试技术 持续交付
ONNX 与持续集成/持续部署 (CI/CD):构建可信赖的 ML 生命周期管理
【8月更文第27天】随着机器学习 (ML) 模型的广泛应用,确保模型的正确性、稳定性和可追踪性变得尤为重要。持续集成/持续部署 (CI/CD) 是软件开发中的重要实践,旨在通过自动化测试和部署流程来提高软件质量和开发效率。将 ONNX 集成到 CI/CD 流程中可以实现模型版本管理、自动化测试和部署,从而构建一个可信赖的机器学习生命周期管理系统。本文将探讨如何将 ONNX 模型与 CI/CD 流程结合,以实现模型的自动化管理。
228 5
|
监控 供应链 数据挖掘
ERP系统中的成本控制与降低成本策略解析
【7月更文挑战第25天】 ERP系统中的成本控制与降低成本策略解析
1093 3
|
运维 应用服务中间件 数据库
深入解析现代运维中的自动化工具应用
在现代运维领域,自动化工具成为提高工作效率和降低人为错误的关键因素。本文将探讨几种常见的运维自动化工具,它们的功能、优势及其在实际应用中的案例,以期为运维人员提供有价值的参考。
207 0