【数据结构和算法初阶(C语言)】空间复杂度(例题剖析一起探究空间如何评价算法)

目录

1.衔接前言-时间复杂度的回顾 

2.关于算法复杂度

3.本文主角-空间复杂度

3.1大O的渐进表示方法

4.空间复杂度例题----实际感受空间复杂度

4.1冒泡排序的空间复杂度

4.2计算递归函数的空间复杂度 

4.3动态开辟内存版本求斐波那契数列的空间复杂度

4.4(重要难点理解)求斐波那契数列递归算法的时间复杂度(注意空间复用)

5.常见复杂度对比

6.关于空间复杂度的oj题目练习-旋转数组

6.1思路1

6.2思路2

6.2.1k%n的重要性-----oj题目中的越界错误

6.3思路3-----旋转旋转再旋转

7.结语


 

1.衔接前言-时间复杂度的回顾 

时间复杂度复习跳转

2.关于算法复杂度

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

3.本文主角-空间复杂度

  • 空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度 
  • 空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。形式参数不算。
  • 空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。
  • 注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因 此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。但是如果在运行期间额外调用堆栈要算

3.1大O的渐进表示方法

  • 这个表示方法在时间复杂度讲过大家也可以先看完时间复杂度在过来。
  • 1、用常数1取代运行时间中的所有加法常数。
  • 2、在修改后的运行次数函数中,只保留最高阶项。
  • 3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

我们直接上代码来带入理解时间复杂度的计算方法:

4.空间复杂度例题----实际感受空间复杂度

重点

运行过程中临时占用存储空间大小的量度

①空间复杂度算的运行过程中开辟的变量的个数。开辟的变量要满足要求就是为了实现这个算法,形式参数不算。

②运行期间额外调用堆栈要算

4.1冒泡排序的空间复杂度

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

那我们首先看一下我们调用这个算法,会创建那几个变量:

  • ①所示:end、exchange、i这三个变量就是我们的为了实现算法而创建的变量,分别为了实现我们算法中的循环、迭代等功能。所以按照大O的渐进表示法,这个算法的空间复杂度就是O(3),但是,根据第一条:用常数1取代运行时间中的所有加法常数。所以这个冒泡排序算法的空间复杂度表示为O(1).
  • ②这是一个形式参数,不算做空间复杂度的计算对象
  • ③这是一个指针参数,指向一个数组,很多伙伴会想这个数组的空间要不要算作空间复杂度的计算之中,实际上是不必要的,我们要牢牢扣住空间复杂度的定义,为了实现算法而额外开辟的空间才纳入计算,那么如果我们不是为了实现这算法而是要调用其他算法,那么这个数组的空间照样要上传到其他算法1,所以这个数组的空间就不算做时间复杂度的值内。

4.2计算递归函数的空间复杂度 

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

4.3动态开辟内存版本求斐波那契数列的空间复杂度

// 计算Fibonacci的空间复杂度?
// 返回斐波那契数列的前n项
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;
}

4.4(重要难点理解)求斐波那契数列递归算法的时间复杂度(注意空间复用)

重点:计算时间复杂度的时候时间是可以累加的,但是空间却是可以重复利用的

先上代码:

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

我们在计算其时间复杂度的时候我们这样来理解这个算法的调用的:

在这个时候我们理解的是:递归调用函数是一起调用的,但是在真正的递归在内存中跑起来却不是这样调用的:

我们以Fib(4)举例讲解:

然后后面的调用都是使用这片空间,我们如果在程序中调试去看,我们的N的值应该会这样变化:4-3-2-1-3-4-2-4:

那么当我们有n个递归:

是不是只会总的开辟N个空间从N到1,那么空间复杂度就为O(N)

5.常见复杂度对比

6.关于空间复杂度的oj题目练习-旋转数组

做题链接

可以参考字符串左旋的解题过程:字符串左旋详细讲解 

要求时间复杂度O(N)

空间复杂度O(1)

6.1思路1

我们将最后一个数存放在一个变量之中,然后我们将前面的数往后挪,再把最后一个数放在第一个就完成了一次左旋,循环k次就完成了k次左旋。可参考左旋的这个图解,注意方向相反就行,原理一样:

那么此时我们计算一下

  • 时间复杂度:循环K次,由于假如我们四个数,我们是不是最多可以右旋3次,旋转第四次就得到原数组内容,那么我们最多旋转k%N次,最大为N-1,我们交换需要交换n次最多,那么时间复杂度就是(N-1)^2,就是O(N).
  • 接下来我们估算一下空间复杂度,由于我们没有额外申请空间,创建一个temp变量用于交换,创建i变量用于循环,我们的空间复杂度为常数个就是O(1),
  • 那么测试用例过不了应该是因为时间复杂度。

6.2思路2

时间复杂度:使用拷贝函数的话最坏遍历循环n次,调用三次拷贝函数,为3n,时间复杂度为O(N).

空间复杂度:我们额外申请了numsize个空间和创建一些变量,空间复杂度应该为N+1,就是O(N).

空间消耗很多注意。

6.2.1k%n的重要性-----oj题目中的越界错误

在代码中K%n是必要的,因为我们知道旋转最多旋转n-1次,因为选择n次相当于回到原点,如果我们不模出,就会出现越界问题。我们可以看一下这个错误的代码:

6.3思路3-----旋转旋转再旋转

参考我们的左旋思路:

  • 我们分析一下时间复杂度:每一次调用我们的旋转函数,是不是最坏的情况就是循环n-1次,调用三次旋转函数,就是3n-3,那么我们的时间复杂度就为O(N)
  • 空间复杂度:我们只是创建了一些变量,额外写了旋转函数,在栈上额外开辟了一个空间,那么额外空间开辟就是常数个,空间复杂度就是O(1). 

我们实现一下:

7.结语

以上就是本期的所有内容,知识含量蛮多,大家可以配合解释和原码运行理解。创作不易,大家如果觉得还可以的话,欢迎大家三连,有问题的地方欢迎大家指正,一起交流学习,一起成长,我是Nicn,正在c++方向前行的奋斗者,数据结构内容持续更新中,感谢大家的关注与喜欢。

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

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

相关文章

蓝桥杯_定时器的基本原理与应用

一 什么是定时器 定时器/计数器是一种能够对内部时钟信号或外部输入信号进行计数&#xff0c;当计数值达到设定要求时&#xff0c;向cpu提出中断处理请求&#xff0c;从而实现&#xff0c;定时或者计数功能的外设。 二 51单片机的定时/计数器 单片机外部晶振12MHZ&#xff0c;…

如何实现双向循环链表

博主主页&#xff1a;17_Kevin-CSDN博客 收录专栏&#xff1a;《数据结构》 引言 双向带头循环链表是一种常见的数据结构&#xff0c;它具有双向遍历的特性&#xff0c;并且在表头和表尾之间形成一个循环。本文将深入探讨双向带头循环链表的结构、操作和应用场景&#xff0c;帮…

el-table通过这样封装可以实现校验-表格校验的原理

我们一般在后台系统中&#xff0c;很常见的操作时表格里面嵌套表单&#xff0c;之前我的网上找到了一些封装的用法&#xff1a; <el-form :model"formData" :rules"ruleData" ref"formDom"><el-table :data"formData.tableData&q…

Vue.js入门指南:简介、环境配置与Yarn创建项目

一、Vue.js简介 Vue.js&#xff0c;一个流行的JavaScript框架&#xff0c;以其直观、灵活和高效的特点&#xff0c;在前端开发者中赢得了广泛的赞誉。Vue.js的核心库专注于视图层&#xff0c;使得开发者能够构建出响应式的数据绑定和组合的视图组件。Vue.js的目标是通过尽可能简…

CPU、GPU 混合推理,非常见大模型量化方案:“二三五六” 位量化,模型量化详细实现方案

CPU、GPU 混合推理&#xff0c;非常见大模型量化方案&#xff1a;“二三五六” 位量化&#xff0c;模型量化详细实现方案。 非常见整型位数的量化&#xff0c;来自让各种开源模型能够在 CPU 环境、CPU & GPU 环境混合推理的技术方案&#xff1a;llama.cpp 。为了能够在低配…

iOS群控软件功能分析与代码分享!

随着移动互联网的迅猛发展&#xff0c;iOS设备作为市场上一大主流平台&#xff0c;其应用开发和管理越来越受到开发者和企业的重视&#xff0c;iOS群控软件&#xff0c;作为一种能够批量控制、管理和监控iOS设备的工具&#xff0c;逐渐展现出其强大的实用价值。 本文将详细分析…

React回顾

一、基础 1、使用babel解析 2、不直接使用jsx&#xff0c;jsx写起来很繁琐 3、jsx语法规则 4、函数式组件的使用 5、函数式组件渲染 6、类组件渲染 7、类组件中事件调用this指向问题 8、类组件不能直接改变状态 9、props接收数据类型限制 类型限制放到类组件内部&#xff0c;用…

StarRocks实战——滴滴OLAP的技术实践与发展方向

原文大佬的这篇StarRocks实践文章整体写的很深入&#xff0c;介绍了StarRocks数仓架构设计、物化视图加速实时看板、全局字典精确去重等内容&#xff0c;这里直接摘抄下来用作学习和知识沉淀。 目录 一、背景介绍 1.1 滴滴OLAP的发展历程 1.2 OLAP引擎存在的痛点 1.2.1 运维…

IOC 和 AOP

IOC 所谓的IOC&#xff08;inversion of control&#xff09;&#xff0c;就是控制反转的意思。何为控制反转&#xff1f; 在传统的程序设计中&#xff0c;应用程序代码通常控制着对象的创建和管理。例如&#xff0c;一个对象需要依赖其他对象&#xff0c;那么它会直接new出来…

express+mysql+vue,从零搭建一个商城管理系统6--数据校验和登录

提示&#xff1a;学习express&#xff0c;搭建管理系统 文章目录 前言一、修改models/user.js二、修改routes下的user.js三、Api新建user/login接口四、删除数据库原有数据&#xff0c;添加新验证规则的用户四、用户登录总结 前言 需求&#xff1a;主要学习express&#xff0c;…

【Linux】基础篇-Linux四种环境搭建的方式(详细安装说明步骤,搭载下载安装地址)

目录 1. 使用虚拟机&#xff08;推荐VMware&#xff09;centos 7版本 1.1VMware虚拟机下载 1.2VMware 安装 1.3centos-7 清华大学镜像下载 1.4 centos-7 清华大学镜像导入虚拟机VMware 2.使用虚拟机ubuntu 20.04版本 2.1虚拟机下载同上 2.2虚拟机安装同上 2.3ubunt…

ROS-Ubuntu 版本相关

ROS-Ubuntu 版本相关&#xff1a;安装指引 年代ROS1版本Ubuntu 版本2014Indigo14.042016Kinetic16.042018Melodic18.042020Noetic20.04 & 22.04 ROS2兼顾了工业使用上的问题。 年代ROS2版本Ubuntu 版本2022Humble20.04 & 22.042023Iron16.04 相关参考&#xff1a; […

【Qt 学习之路】使用 cmake 在Windows上 编译 ZeroMQ

文章目录 1、概述2、编译2.1、用 Visual Studio 的解决方案方式2.1.1、找到 Builds 文件夹2.1.2、查看 deprecated-msvc 下的 libzmq.sln 文件2.1.3、使用 Visual Studio 打开 libzmq.sln 解决方案2.1.4、修改 libzmq.import.props 文件2.1.5、编译生成 2.2、用 C 的cmake方式2…

【前端入门】设计模式+单多页+React

设计模式是一种解决特定问题的经验总结&#xff0c;它提供了经过验证的解决方案&#xff0c;可以在软件开发过程中使用。设计模式可以帮助前端开发人员更有效地组织和管理代码&#xff0c;并提供一种共享的语言和框架&#xff0c;以便与其他开发人员进行交流。 以下是一些常见…

十二、Qt自定义Widget组件、静态库与动态库

一、自定义Widget组件 1、自定义Widget组件 使用步骤采用提升法&#xff08;promotion&#xff09;重新定义paintEvent事件 2、实现程序 &#xff08;1&#xff09;创建项目&#xff0c;基于QWidget &#xff08;2&#xff09;添加类&#xff0c;为Widget组件提升类 #inclu…

Delegate(P29 5.5delegate)

一、Delegate简介 每个代理都可以访问许多附加的属性&#xff0c;其中一些来自数据模型&#xff0c;另一些来自视图。 从模型中&#xff08;Model&#xff09;&#xff1a;属性将每个项目的数据传递给 delegate。 从视图中&#xff08;View&#xff09;&#xff1a;属性将状…

dcat admin 自定义页面

自定义用户详情页 整体分为两部分&#xff1a;用户信息、tab框 用户信息采用自定义页面加载&#xff0c;controller代码如下&#xff1a; protected function detail($id) {return Show::make($id, GameUser::with(finance), function (Show $show) {// 这段就是加载自定义页面…

pdf怎么合并在一起?

pdf怎么合并在一起&#xff1f;在日常工作和学习中&#xff0c;我们常常需要处理大量的PDF文件。有时候&#xff0c;我们可能希望将多个PDF文件合并成一个文件&#xff0c;以便于管理和分享。这时候&#xff0c;PDF文件合并工具就能派上用场了。PDF文件合并是一种将多个PDF文件…

MySQL 事务原理分析

事务 前提&#xff1a;有并发连接。定义&#xff1a;事务是用户定义的一系列操作&#xff0c;这些操作要么都做&#xff0c;要么都不做&#xff0c;是一个不可分割的单位。目的&#xff1a;事务将数据库从一种一致性状态转换为另一种一致性状态&#xff0c;保证系统始终处于一…

【数据结构】从链表到LinkedList类

&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;个人主页&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388; &#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;&#x1f9e7;数据结构专栏&#x1f388;&#x1f388;&#x1f388;&…