数据结构 — 时间复杂度、空间复杂度

news2024/12/24 8:29:50

前言

数据结构_空间复杂度_时间复杂度讲解_常见复杂度对比

本文介绍数据结构中的时间复杂度和空间复杂度
***文章末尾,博主进行了概要总结,可以直接看总结部分***

博主博客链接:https://blog.csdn.net/m0_74014525
点点关注,后期持续更新系列文章


文章目录

  • 前言
  • 一、算法效率
  • 二、时间复杂度
    • 1. 时间复杂度的定义
    • 2. 大O的渐进表示法
    • 3. 时间复杂度计算举例
  • 三、空间复杂度
    • 1. 空间复杂度的定义
    • 2. 空间复杂度计算举例
  • 四、常见复杂度对比
    • 1. 常见的时间复杂度及其对应的算法
    • 2. 常见复杂度的对比
  • 总结


一、算法效率

算法效率指的是算法在处理数据时所消耗的时间和空间资源的多少。

算法效率通常由时间复杂度空间复杂度来衡量。

时间复杂度: 表示算法在处理数据时所需要的运算次数与数据规模之间的关系。

空间复杂度: 表示算法在处理数据时所需要的存储空间与数据规模之间的关系。

算法效率的好坏直接影响到算法的执行速度和资源利用率,是算法设计时需要考虑的重要因素之一。


二、时间复杂度

1. 时间复杂度的定义

时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。

一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。

一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。

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

举例说明:

// 请计算一下Func1中++count语句总共执行了多少次?
void Func1(int N)
{
	int count = 0;
	for (int i = 0; i < N; ++i)
	{									//外层循环n次
		for (int j = 0; j < N; ++j)
		{
			++count;					//内层循环n次
										//n*n
		}
	}

	for (int k = 0; k < 2 * N; ++k)
	{
		++count;					//2*N 次
	}
	int M = 10;
	while (M--)
	{
		++count;					//循环10次
	}
	printf("%d\n", count);
}

Func1 执行的基本操作次数 :

F ( N ) = N 2 + 2 ∗ N + 10 F(N)=N^2+2*N+10 F(N)=N2+2N+10
N = 10 F(N) = 130
N = 100 F(N) = 10210
N = 1000 F(N) = 1002010
实际中我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数,而只需要大概执行次数,那么这里我们使用大O的渐进表示法。

使用大O的渐进表示法以后,Func1的时间复杂度为:

O ( N 2 ) O(N^2) O(N2)

N = 10 F(N) = 100
N = 100 F(N) = 10000
N = 1000 F(N) = 1000000
通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项,简洁明了的表示出了执行次数。


2. 大O的渐进表示法

大O表示法是算法时间复杂度的渐进表示法,表示算法的运行时间在最坏情况下的渐进上限。

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

推导大O阶方法:
1、用常数1取代运行时间中的所有加法常数。
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。

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

最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)

在实际中一般情况关注的是算法的最坏运行情况
需要注意的是,大O表示法只关注算法的渐进时间复杂度,而不关注算法的具体实现细节和常数项。因此,两个算法的时间复杂度可能相同,但它们的实际运行时间可能差别很大,这需要具体问题具体分析。


3. 时间复杂度计算举例

实例1:

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

实例1解析:
上述算法基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N)


实例2:

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

实例2解析:
上述算法基本操作执行了M+N次,有两个未知数M和N,时间复杂度为 O(N+M)


实例3:

// 计算Func4的时间复杂度?
void Func4(int N)
{
	int count = 0;
	for (int k = 0; k < 100; ++k)
	{
		++count;
	}
	printf("%d\n", count);
}

实例3解析:
上述算法基本操作执行了10次,通过推导大O阶方法,时间复杂度为 O(1)


实例4:

// 计算strchr的时间复杂度?
const char * strchr ( const char * str, int character );

实例4解析:
上述算法基本操作执行最好1次,最坏N次,时间复杂度一般看最坏,时间复杂度为 O(N)


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

实例5解析:
上述算法基本操作执行最好N次,最坏执行了(N*(N+1)/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N^2)


实例6:

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

实例6解析:
上述算法基本操作执行最好1次,最坏O(logN)次,时间复杂度为 O(logN)
ps:logN在算法分析中表示是底数为2,对数为N。有些地方会写成lgN。(建议通过折纸查找的方式讲解logN是怎么计算出的)


实例7:

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

	return Fac(N - 1) * N;
}

实例7解析:
上述算法通过计算分析发现基本操作递归了N次,时间复杂度为O(N)。


实例8:

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

	return Fib(N - 1) + Fib(N - 2);
}

实例8解析:
实例8通过计算分析发现基本操作递归了2^N次,时间复杂度为O(2^N)


三、空间复杂度

1. 空间复杂度的定义

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度 。

空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法

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


2. 空间复杂度计算举例

实例1:

// 计算BubbleSort的空间复杂度?
void BubbleSort(int* a, int n)
{
	assert(a);
	for (size_t end = n; end > 0; --end)   		//end
	{
		int exchange = 0;           			//exchange
		for (size_t i = 1; i < end; ++i)		//i   
		{
			if (a[i - 1] > a[i])
			{
				Swap(&a[i - 1], &a[i]);
				exchange = 1;
			}
		}
		if (exchange == 0)
			break;
	}
}

实例1解析:
上面算法里用三个变量,为常数个变量
使用了常数个额外空间,所以空间复杂度为 O(1)


实例2:

// 计算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解析:
上面算法使用malloc动态内存函数开辟了N个空间
动态开辟了N个空间,空间复杂度为 O(N)


实例3:

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

	return Fac(N - 1) * N;
}

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


四、常见复杂度对比

1. 常见的时间复杂度及其对应的算法

大O渐进表示法时间复杂度类型算法的执行时间与输入规模关系对应算法
O(1)常数时间复杂度表示算法的执行时间与输入规模无关例如:访问数组元素、哈希表操作等。
O(log n)对数时间复杂度表示算法的执行时间与输入规模的对数相关例如:二分查找、平衡二叉树操作等。
O(n)线性时间复杂度表示算法的执行时间与输入规模成线性关系例如:线性查找、顺序遍历数组等。
O(n log n)线性对数时间复杂度表示算法的执行时间介于线性和对数之间例如:归并排序、快速排序等。
O(n^2)平方时间复杂度表示算法的执行时间与输入规模的平方相关例如:冒泡排序、选择排序等。
O(n^3)立方时间复杂度表示算法的执行时间与输入规模的立方相关例如:矩阵乘法等。
O(2^n)指数时间复杂度表示算法的执行时间与输入规模的指数相关例如:求解子集、穷举等。

2. 常见复杂度的对比

在这里插入图片描述


总结

时间复杂度和空间复杂度是用来衡量算法性能的两个重要指标。

时间复杂度表示算法的运行时间随着数据规模的增长而增长的趋势,通常用大O表示法来表示。时间复杂度越低,算法的效率越高。时间复杂度和数据规模之间的关系如下:

  • 常数时间:O(1)
  • 对数时间:O(log n)
  • 线性时间:O(n)
  • 线性对数时间:O(n log n)
  • 平方时间:O(n^2)
  • 立方时间:O(n^3)
  • 指数时间:O(2^n)

空间复杂度表示算法所需内存空间增长的趋势,同样用大O表示法来表示。空间复杂度越低,算法所需内存空间越少。空间复杂度和数据规模之间的关系如下:

  • 常数空间:O(1)
  • 线性空间:O(n)
  • 平方空间:O(n^2)

在实际应用中,我们要综合考虑时间复杂度和空间复杂度,找到一个性能和资源占用的平衡点,以实现最优的算法效果。时间复杂度和空间复杂度是用来衡量算法性能的两个重要指标。


如这篇博客对大家有帮助的话,希望 三连 支持 !!!

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

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

相关文章

STL文件格式详解【3D】

STL&#xff08;StereoLithography&#xff1a;立体光刻&#xff09;文件是 3 维表面几何形状的三角形表示。 表面被逻辑地细分或分解为一系列小三角形&#xff08;面&#xff09;。 每个面由垂直方向和代表三角形顶点&#xff08;角&#xff09;的三个点来描述。 切片算法使用…

YOLOv5修改注意力机制CBAM

直接上干货 CBAM注意力机制是由通道注意力机制&#xff08;channel&#xff09;和空间注意力机制&#xff08;spatial&#xff09;组成。 传统基于卷积神经网络的注意力机制更多的是关注对通道域的分析&#xff0c;局限于考虑特征图通道之间的作用关系。CBAM从 channel 和 sp…

Opencv将数据保存到xml、yaml / 从xml、yaml读取数据

Opencv将数据保存到xml、yaml / 从xml、yaml读取数据 Opencv提供了读写xml、yaml的类实现&#xff1a; 本文重点参考&#xff1a;https://blog.csdn.net/cd_yourheart/article/details/122705776?spm1001.2014.3001.5506&#xff0c;并将给出文件读写的具体使用实例。 1. 官…

Android多渠道打包+自动签名工具 [原创]

多渠道打包自动签名工具 [原创] github源码&#xff1a;github.com/G452/apk-packer 如果觉得有帮助可以点个小星星支持一下&#xff0c;万分感谢&#xff01; 使用步骤&#xff1a; 1、在apk-packer.exe目录内放入打包需要的配置&#xff1a; 1&#xff09;签名文件.jks2&am…

undefined reference to `dlopen‘ ‘SSL_library_init‘ `X509_certificate_type‘

使用Crow的时候需要注意crow依赖asio依赖OpenSSL&#xff0c;asio要求1.22以上版本&#xff0c;我使用的是1.26.0&#xff1b; 这个版本的asio要求OpenSSL是1.0.2&#xff0c;其他版本我得机器上编不过&#xff0c;ubuntu上默认带的OpenSSL是1.1.1; 所以我下载了OPENSSL1.2.0重…

【Linux】TCP协议的相关实验——深入理解

TCP相关实验 理解CLOSE_WAIT状态 当客户端和服务器在进行TCP通信时&#xff0c;如果客户端调用close函数关闭对应的文件描述符&#xff0c;此时客户端底层操作系统就会向服务器发起FIN请求&#xff0c;服务器收到该请求后会对其进行ACK响应。 但如果当服务器收到客户端的FIN…

【LeetCode每日一题】——205.同构字符串

文章目录 一【题目类别】二【题目难度】三【题目编号】四【题目描述】五【题目示例】六【题目提示】七【解题思路】八【时间频度】九【代码实现】十【提交结果】 一【题目类别】 哈希表 二【题目难度】 简单 三【题目编号】 205.同构字符串 四【题目描述】 给定两个字符…

Appium - 移动端自动测试框架,如何入门?

Appium是一个开源跨平台移动应用自动化测试框架。 既然只是想学习下Appium如何入门&#xff0c;那么我们就直奔主题。文章结构如下&#xff1a; 1、为什么要使用Appium&#xff1f; 2、如何搭建Appium工具环境?(超详细&#xff09; 3、通过demo演示Appium的使用 4、Appium如何…

【C++常见八股1】内存布局 | 参数压栈 | 构造析构调用 | 空类大小

内存布局 .text 代码段&#xff1a;存放二进制代码、字符串常量.data 段&#xff1a;存放已初始化全局变量、静态变量、常量.bss 段&#xff1a;未初始化全局变量&#xff0c;未初始化静态变量heap 堆区&#xff1a;new/malloc 手动分配的内存&#xff0c;需要手动释放stack 栈…

竞赛项目 深度学习图像风格迁移

文章目录 0 前言1 VGG网络2 风格迁移3 内容损失4 风格损失5 主代码实现6 迁移模型实现7 效果展示8 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习图像风格迁移 - opencv python 该项目较为新颖&#xff0c;适合作为竞赛课题…

Redis_概述

1.redis概述 1.1 简介 截止到2021年12月 数据库排名https://db-engines.com/en/ranking redis(Remote Dictionary Server) 一个开源的key-value存储系统它支持存储的Value类型&#xff1a;包括String(字符串),list(链表),set(集合),zset(sorted set 有序集合),hash(哈希类型…

【C++11】列表初始化 | decltype操作符 | nullptr | STL的更新

文章目录 一.列表初始化1. 花括号初始化2. initializer_list 二.decltype三.nullptr四.STL的更新1.STL新增容器2.字符串转换函数3.容器中的一些新方法 一.列表初始化 1. 花括号初始化 { }的初始化 C98中&#xff0c;标准允许使用大括号{}对数组或者结构体元素进行统一的列表初…

Unity游戏源码分享-俄罗斯方块unity2017

Unity游戏源码分享-俄罗斯方块unity2017 工程地址&#xff1a; https://download.csdn.net/download/Highning0007/88204011

STM32 F103C8T6学习笔记3:串口配置—串口收发—自定义Printf函数

今日学习使用STM32 C8T6的串口&#xff0c;我们在经过学习笔记2的总结归纳可知&#xff0c;STM32 C8T6最小系统板上有三路串口&#xff0c;如下图&#xff1a; 今日我们就着手学习如何配置开通这些串口进行收发&#xff0c;这里不讲串口通信概念与基础&#xff0c;可以自行网上…

突破笔试:力扣全排列(medium)

1. 题目链接&#xff1a;46. 全排列 2. 题目描述&#xff1a;给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[…

Redis_安装、启动以及基本命令

2.Redis安装 2.1前置处理环境 VMware安装安装centOS的linux操作系统xshellxftp 2.2 配置虚拟机网络 按ctrlaltf2 切换到命令行 cd (/)目录 修改/etc/sysconfig/network-scripts/ifcfg-ens3 vi 命令 按insert表示插入 按ctrlesc退出修改状态 :wq 写入并退出 此文件必须保持一…

linux鲁班猫代码初尝试[编译镜像][修改根文件系统重编译]

编译镜像 官方百度云盘资料:https://doc.embedfire.com/linux/rk356x/quick_start/zh/latest/quick_start/baidu_cloud/baidu_cloud.html 解压虚拟机压缩包:"鲁班猫\8-SDK源码压缩包\开发环境虚拟机镜像\ubuntu20.04.7z"后既可以用VMware打开,打开后可以看到已经有…

探索数据之美:初步学习 Python 柱状图绘制

文章目录 一 基础柱状图1.1 创建简单柱状图1.2 反转x和y轴1.3 数值标签在右侧1.4 演示结果 二 基础时间线柱状图2.1 创建时间线2.2 时间线主题设置取值表2.3 演示结果 三 GDP动态柱状图绘制3.1 需求分析3.2 数据文件内容3.3 列表排序方法3.4 参考代码3.5 运行结果 一 基础柱状图…

libheif—— 1、Vs2017搭建libheif开发环境

HEIF&#xff08;高效图像文件格式&#xff09; 一种图片有损压缩格式&#xff0c;它的后缀名通常为".heic"或".heif"。 HEIF 是由运动图像专家组 &#xff08;MPEG&#xff09; 标准化的视觉媒体容器格式&#xff0c;用于存储和共享图像和图像序列。它基于…

第二章:CSS基础进阶-part1:CSS高级选择器

文章目录 一、 组合选择器二、属性选择器三、伪类选择器1、动态伪类选择器2、状态伪类选择器3、结构性伪类选择器4、否定伪类选择器 一、 组合选择器 后代选择器&#xff1a;E F子元素选择器&#xff1a; E>F相邻兄弟选择器&#xff1a;EF群组选择器&#xff1a;多个选择器…