数据结构——算法的时间复杂度和空间复杂度

news2025/2/27 17:04:20

文章目录

  • 算法效率
  • 时间复杂度
    • 时间复杂度的定义
    • 大O渐进渐进表示法
    • 常见的时间复杂度计算举例
  • 空间复杂度

算法效率

我们在算法的设计中,会去衡量算法的好坏。那么算法效率就是衡量标准之一。除此之外,我们也会关注到算法的健壮性、正确性、可读性、效率与低存储量的要求。这里的效率和低存储量要求可以引入两个名词,时间复杂度和空间复杂度。通俗来讲,效率指的是算法执行时间。对于同一个问题如果有多个算法可以解决,执行时间短的算法效率高。存储量需求指算法执行过程中所需要的最大存储空间。效率与低存储量需求这两者都与问题的规模有关。

时间复杂度

时间复杂度的定义

一般情况下,算法中基本操作重复执行的次数是为题规模n的某个函数 f(n)
,算法的时间量度记作 T ( n ) = O ( f ( n ) ) T(n) = O(f(n)) T(n)=O(f(n) 它表示随问题规模n的增大,算法执行时间的增长率和f(n)的增长率相同,称作算法的渐进时间复杂度,简称时间复杂度
我们来进行实操

// 请计算一下Func1中++count语句总共执行了多少次?
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;
}
printf("%d\n", count);

算出来的结果是:
F ( N ) = N 2 + 2 N + M F(N) = N^2 +2N + M F(N)=N2+2N+M

实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这 里我们使用大O的渐进表示法。

大O渐进渐进表示法

大O符号(Big O notation):是用于描述函数渐进行为的数学符号。
推导大O阶方法:
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
使用大O的渐进表示法以后,Func1的时间复杂度为:
O ( N 2 ) O(N^2) O(N2)
通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。
另外有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
例如:在一个长度为N数组中搜索一个数据x
最好情况:1次找到
最坏情况:N次找到
平均情况:N/2次找到
在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)

常见的时间复杂度计算举例

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);
}
// 计算Func3的时间复杂度?
void Func3(int N, int M)
{
	int count = 0;
	for (int k = 0; k < M; ++k)
	{
		++count;
	}
	for (int k = 0; k < N; ++k)
	{
		++count;
	}
	printf("%d\n", count);
}

Func2 的第一个循环会执行2N次,第二个循环会执行M次, Func2 的时间复杂度为: 2N+M, 用大O渐进表示法得:
O ( N ) O(N) O(N)
Func3 的第一个循环执行了M次,第二个循环执行了N次,Func3的时间复杂度为: M+N ,用大O渐进法表示法得:
O ( M + N ) O(M+N) O(M+N)
这里除非是题目中规定了N比M大,那么N的影响是最大的。时间复杂度为:
O ( N ) O(N) O(N)

const char * strchr ( const char * str, int character );

这是一个C语言的库函数,它的作用是返回一个字符在字符串中的位置。并且返回这个字符的起始位置,我们可以去这样实现:

const char* my_strchr(const char* str, int character)
{
	while (*str)
	{
		if (*str == character)
		{
			return str;
		}
		str++;
	}
}

对于这个算法的时间复杂度,取决于字符串的大小,因为字符串越长遍历次数越多。还取决于character的位置,character越在字符串的后面,遍历次数越多,语句的执行次数也就相应的增加了。
这个算法的时间复杂度为:
O ( N ) O(N) O(N)

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

冒泡排序在执行的过程中,第一次循环需要执行n-1次,第二次执行n-2次,第三次执行n-3次,第四次执行n-4次……一直到执行1次。把所有的执行次数加起来,这是一个等差数列。可以算出语句的频度为 n*(n-1)/2次。用大O渐进表示法表示:
O ( N 2 ) O(N^2) O(N2)

// 计算BinarySearch的时间复杂度?
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;
}

这个算法为二分查找算法,比如有N个数据,要查找数据的最差的情况是在这串数据的最前边和最后边,那么我们必须把mid移到那一个想要查找的数据上面,所以,假如循环执行了x次。那么,N = 2^x,也就是x个2相乘=N ,语句的执行频度为 x = log以2为底N的对数。用大O渐进表示法可表示为;
O ( l o g N ) O(logN) O(logN)

// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{
 	if(0 == N)
 		return 1;
 
 	return Fac(N-1)*N;
}

我们可以知道以上的程序是求N的阶乘的递归算法,那么我们就要算出他递归了几次,就可以算出它的时间复杂度。分析: 当N = 3 的时候,我们可以拆解为, 3 ∗ F a c ( 2 ∗ F a c ( 1 ∗ F a c ( 0 ) ) ) 3*Fac(2*Fac(1*Fac(0))) 3Fac(2Fac(1Fac(0)))
我们可以看到,这个算法递归了N次,所以用大O渐进表示法可以表示为 O ( N ) O(N) O(N)

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

我们可以使用二叉树的性质来做这道题,考虑最坏的情况,这个二叉树的是一个满二叉树,那么它的最大节点个数为:
2 k − 1 2^k -1 2k1 个,其中k表示深度。那么它的递归次数就是 2 k − 1 2^k-1 2k1次。然后用大O渐进表示法可以表示为 O ( 2 k ) O(2^k) O(2k)
具体可以看这个图,我们假如N 为5。
3>在这里插入图片描述

空间复杂度

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

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

实例2动态开辟了N个空间,空间复杂度为 O(N)

// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N)
{
 	if(N == 0)
		 return 1;
 
 	return Fac(N-1)*N;
}

实例3递归调用了N次,开辟了N个栈帧,每个栈帧使用了常数个空间。空间复杂度为O(N)

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

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

相关文章

C语言课设项目-51单片机-定时器和计数器

(创作不易&#xff0c;感谢有你&#xff0c;你的支持&#xff0c;就是我前行的最大动力&#xff0c;如果看完对你有帮助&#xff0c;请留下您的足迹&#xff09; 目录 一、定时器的原理 1、CPU时序的有关知识 2、在学习定时器之前需要明白的 3、定时/计数器的工作原理 4、…

Mybatis(八)动态Sql的实现原理

一、动态Sql的使用 顾名思义&#xff0c;动态sql值得是事先无法预知具体条件&#xff0c;需要在运行时根据具体的情况动态生成Sql语句。例如: <sql id"userAllField">id,create_time, name, password, phone, nick_name</sql><select id"getUse…

通过工具生成指定 类型 大小 文件

今天给大家介绍一个神器 首先 大家在开发过程中或许经常需要涉及到文件上传类的功能 需要测试文件过大 空文件等等清空 不同大小的文件 而这种文件大小是比较不好控制的 但大家可以下载我的资源 文件生成工具(可生成指定大小 类型文件) 下载下来里面就有一个 fileGeneration…

Mysql的用户管理与权限管理

文章目录用户管理创建用户查看用户字段解释hostUserselect_priv , insert_priv等修改密码修改用户删除用户权限管理查看权限查看当前用户权限查看某用户的全局权限查看某用户的某库的权限查看某用户的某个表的权限授予权限例一例二例三收回权限例一例二专栏目录请点击 用户管理…

目标检测框架yolov5环境搭建

目前&#xff0c;目标检测框架中&#xff0c;yolov5 是很火的&#xff0c;它基于pytorch框架&#xff0c;集成opencv等框架&#xff0c;项目地址&#xff1a;https://github.com/ultralytics/yolov5&#xff0c;对我来说&#xff0c;机器学习、深度学习才开始接触&#xff0c;本…

web前端 --- 表单标签

表单标签 --- 行内标签 描述&#xff1a;一个完整的表单标签通常由表单域、表单控件&#xff08;表单元素&#xff09;和提示信息三部分构成 作用&#xff1a;数据交互&#xff08;C/S&#xff09; &#xff08;1&#xff09;表单域 --- <form> <form>标签用于定义…

一款适合程序员的 Markdown 简历模版,拒绝花里胡哨

一款适合 IT 行业的 Markdown 简约简历模版&#xff0c;拒绝花里胡哨 开始使用 &#xff08;1&#xff09;安装 Markdown 编辑软件&#xff0c;推荐使用 Typora 编辑器&#xff08;免费版 Typora 百度网盘下载点此下载&#xff09;。 &#xff08;2&#xff09;下载本项目&am…

Ansys Speos | 实现车内氛围灯早期仿真验证

在本例中&#xff0c;将演示如何使用Speos进行RGB(红、绿、蓝)车内环境照明的早期研究&#xff0c;目的是在设计光导之前评估指定位置的照明效果。 使用到的产品Ansys Speos 2022 R2或更高版本&#xff0c;license为Enterprise版本能激活人眼视觉效果。 概览 在汽车行业&#…

Nginx的优化,安全与防盗链

1.Nginx的页面优化 1.1 Nginx的网页压缩 在Nginx的ngx_http_gzip_module压缩模块提供对文件内容压缩的功能。进行相关的配置修改&#xff0c;就能实现Nginx页面的压缩&#xff0c;达到节约带宽&#xff0c;提升用户访问速度 vim /usr/local/nginx/conf/nginx.conf http { ..…

#算法记录 | Day33 贪心算法

1005.K次取反后最大化的数组和 class Solution:def largestSumAfterKNegations(self, A: List[int], K: int) -> int:A sorted(A, keyabs, reverseTrue) # 将A按绝对值从大到小排列for i in range(len(A)):if K > 0 and A[i] < 0:A[i] * -1K - 1if K > 0:A[-1] *…

最基础的electron打包运行配置,以及Electron Builder 和 Electron Forge两者差异

node版本 v16.19.0 开发之前请务必去官网查看node所支持的版本&#xff0c;不然安装会报错 推荐使用 yarn 包管理 引入electron依赖 yarn add electron22.3.6 -D yarn add electron-builder -D创建electron配置文件 我这边为了模块化开发&#xff0c;将electron相关文件放…

Chapter3: Design Creation with RTL(ug949)

&#xff08;3.5&#xff09;Clock Domain Crossing 设计中的跨时钟域电路直接影响设计的可靠性。您可以设计自己的电路&#xff0c;但Vivado design Suite必须识别电路&#xff0c;并且必须正确应用ASYNC_REG属性。Xilinx提供XPM以确保正确的电路设计&#xff0c;包括…

【c/c++编译工具】——Cmake的学习

简介 目录 简介 1. Cmake的基本语法 2. 常用指令 3. CMake常用的变量 4. CMake编译工程 5. 构建方式 6. 实战---CMake代码实战 CMake是一个跨平台的安装编译工具&#xff0c;可以用简单的语句来描述所有平台的安装(编译过程)。CMake可以说已经成为大部分C开源项目标配…

将自己的服务注册成Windows服务

winsw下载地址 https://github.com/winsw/winsw/releases 如何注册服务 将WinSW.exe复制到自定义的目录同目录下创建projectName.xml。特别注意&#xff0c;xml和exe必须同名配置 xml文件 xml配置信息如下 <service><id>wjb-member</id><name>wjb-me…

15-721 Chapter7 索引

锁的分类 锁占用少量内存&#xff0c;实际上在不竞争的情况下性能不错. 第一个就是靠test_and_set实现的自旋锁&#xff0c;高效&#xff0c;因为在用户态&#xff0c;但是却不可扩展&#xff0c;对cache&#xff0c;os都不友好 第二个是mutex&#xff0c;实际上两个部分组成…

浮空,定位

浮动原理&#xff1a; 清除浮动 高度塌陷是怎么造成的&#xff0c;浮动元素脱离当前文档流&#xff0c;然后无法撑起父容器导致了高度塌陷&#xff0c;因为父容器没有设置高度 解决方法&#xff1a; 1.将父元素变成bfc元素 2.父元素浮动 3.清除浮动&#xff1a; 通过给父元…

小伙被内卷逼成扫地僧,把牛客网所有面试题整理成25W字面试手册

很多工作过了 5 年之后的工程师&#xff0c;都表示写不动 CRUD 代码了&#xff0c;都会考虑转架构师&#xff0c;但是一想到面试&#xff0c;可能心里就会一紧。 一般面试大厂架构师的岗位至少需要 3 轮技术面试&#xff0c;我咨询了一下阿里的大佬&#xff0c;了解到每一轮的…

数字化时代,如何建造会“运转”的数仓

在建设数仓之前需要根据数据基础和业务需求来决定要建设什么标准的数仓。 常见数仓问题 1、公共底层加工逻辑分散&#xff1a;对于来自多个数据源&#xff0c;但需要使用相同过滤和解析方式公共底层数仓&#xff0c;其过滤和解析代码在每个任务或配置中直接静态复制&#xff…

【系统集成项目管理工程师】项目范围管理

&#x1f4a5;十大知识领域&#xff1a;项目范围管理 项目范围管理包括以下 6 个过程: 编制范围管理计划过程收集需求过程定义范围过程创建工作分解结构过程确认范围过程范围控制过程 一、编制范围管理计划过程 对如何定义、确认和控制项目范围的过程进行描述 1、输出&#xff…

ch02-PyTorch数据预处理

ch02-PyTorch数据预处理0.引言1.数据读取机制 Dataloader 与 Dataset1.1.纸币二分类1.2.DataSet与DataLoader1.2.1.torch.utils.data.DataLoader&#xff1a;构建可迭代的数据装载器1.2.2.torch.utils.data.Dataset&#xff1a;Dataset抽象类1.2.3.以人民币分类为例2.数据预处理…