【数据结构】复杂度

在这里插入图片描述
🔥博客主页 小羊失眠啦.
🎥系列专栏《C语言》 《数据结构》 《Linux》《Cpolar》
❤️感谢大家点赞👍收藏⭐评论✍️


在这里插入图片描述

文章目录

  • 一、什么是数据结构
  • 二、什么是算法
  • 三、算法的效率
  • 四、时间复杂度
    • 4.1 时间复杂度的概念
    • 4.2 大O渐进表示法
    • 4.2 常见时间复杂度计算
    • 4.3 例题:消失的数字
  • 五、空间复杂度
    • 5.1 空间复杂度计算
    • 5.2 例题:轮转数组

一、什么是数据结构

数据结构(Data Structure)是计算机存储、组织数据的方式,指相互之间存在一种或多种特定关系的数据元素的集合。


二、什么是算法

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


三、算法的效率

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

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


四、时间复杂度

4.1 时间复杂度的概念

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

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

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

(1)时间复杂度表达式为:F(N)=N*N+2*N+10

(2)大O渐进表示法:O(N^2)

这个函数的基本操作次数是:F(N)=N*N+2*N+10,随着N的增大,后两项对整个结果的影响可以忽略不计

4.2 大O渐进表示法

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

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

推导大O阶方法:

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

在这里插入图片描述

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

在这里插入图片描述

  1. 如果最高阶项存在且系数不为1,则去除这个项的系数

在这里插入图片描述

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

  • 最坏情况:

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

  • 平均情况:

    任意输入规模的期望运行次数

  • 最好情况:

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

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

4.2 常见时间复杂度计算

冒泡排序:

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

最好情况O(N)

最好情况就是数组本身有序,虽然它有序,但是计算机最初并不知道它是有序的,仍需要遍历一遍数组才能知道它是有序的,所以就好情况就是O(N)。

在这里插入图片描述最坏情况O(N^2)

最坏情况是数组完全逆序,则第一趟需要交换N − 1 次,第二趟需要交换N − 2次…直到最后一趟只交换一次,把所有的交换次数加起来就得到了冒泡排序最坏情况下的时间复杂度,其实也就是一个等差数列求和,所以最会情况下的时间复杂度是O(N^2)

在这里插入图片描述

二分查找:

// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{
	assert(a);
	int left = 0;
	int right = n - 1;
	while (left <= right)
	{
		int mid = left + ((right - left) >> 1);
		if (a[mid] < x)
			left = mid + 1;
		else if (a[mid] > x)
			right = mid - 1;
		else
			return mid;
	}
	return -1;
}

最好情况O(1)

最好情况是第一次查找就找到目标值,此时时间复杂度就是O(1)。

在这里插入图片描述

最坏情况O(long2N)

二分查找每次可以排出一半的数据,就坏的情况就是排出到只剩下一个数据。当N/2/2/2/2……/2=1时,就找到了目标值。除去了几个2就是执行的次数,所以时间复杂度为O(log2N)。

在这里插入图片描述

O(N)和O(log2N)的对比:

N1000100W10亿
O(N)1000100W10亿
O(log2N)102030

由此我们看到O(log2N)相对O(N)在效率上有很大的提升,但二分查找有一个限制条件就是数组必须有序,所以在实际中二分查找应用并不多。

递归阶乘:

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

时间复杂度: O(N)

Fac一共被递归调用了N+1次,且每次Fac中执行1次,总共执行N+1次,所以时间复杂度是O(N)。

斐波那契数列:

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

时间复杂度: O(2^N)

斐波那契数列,它的时间复杂度是等比数列求和,所以时间复杂度为O(2^N)。

在这里插入图片描述

4.3 例题:消失的数字

在这里插入图片描述

方法一:我们可以把0~N个数字全部加起来,减去数组中的元素,结果就是消失的数字。

时间复杂度为O(N)

int missingNumber(int* nums, int numsSize)
{
    int i=0;
    int ret=(numsSize+1)*numsSize/2;
    for(i=0;i<numsSize;i++)
    {
        ret-=nums[i];
    }
    return ret;
}

方法二: 我们可以使用异或,异或的条件是相同为0,相异为1。两个相同的数异或为0,0和任何数异或都为原数,所以我们将0~N与数组中的所有异或,得到的结果就是消失的数。

int missingNumber(int* nums, int numsSize){
    int i = 0,k = 0;
    for(i=0;i<numsSize;i++)
    {
        k^=nums[i];
    }
    for(i=0;i<numsSize+1;i++)
    {
        k^=i;
    }
    return k;
}

五、空间复杂度

空间复杂度的概念:

空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度 。 空间复杂度不是程序占用了多少bytes的空间,而算的是变量的个数。 空间复杂度计算规则也使用大O渐进表示法。

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

一般常见的空间复杂度都是O(1)或者O(N)(额外开辟数组)。

5.1 空间复杂度计算

冒泡排序:

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),临时占用储存空间的变量有i、exchange、end,一共三个。

递归阶乘:

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

空间复杂度为O(N),函数的每一次调用都会开辟一个栈帧,每个栈帧开常数个空间,开辟N+1个栈帧,空间复杂度就为O(N)。

注意: 递归程序最大的问题就是深度太深,会有栈溢出的风险。

斐波那契数列:

long long Fib(size_t N)
{
 if(N < 3)
 return 1;
 
 return Fib(N-1) + Fib(N-2);
}

空间复杂度为O(N)

空间是可以重复利用的,递归的调用是按一条线递归下去的,不会同时递归,当递归到最后一层返回时,创建的函数栈帧销毁,调用另一条仍可以使用这块空间,所以空间复杂度是O(N)。

在这里插入图片描述

5.2 例题:轮转数组

在这里插入图片描述

方法一:右旋K次(每次右旋一次)【时间复杂度为O(N*K);空间复杂度为O(1)】(时间复杂度的k最大为N-1),最坏的结果为O(N^2))

方法二:开设一个新的数组,把后k个放到新数组的前面,前面的依次放到后面【时间复杂度O(N),空间复杂度O(N)】 (这种方法比第一种,就是时间换取空间的算法)

方法三:三趟逆置【时间复杂度为O(N),空间复杂度为O(1)】

void reverse(int*nums,int* left,int* right)
{
    int tmp=0;
    while(left<=right)
    {
        tmp=*left;
        *left=*right;
        *right=tmp;
        left++;
        right--;
    }
}
void rotate(int* nums, int numsSize, int k)
{
    k%=numsSize;
    int* p=nums;
    reverse(nums,p,p+numsSize-k-1);
    reverse(nums,p+numsSize-k,p+numsSize-1);
    reverse(nums,p,p+numsSize-1);
}

本次的内容到这里就结束啦。希望大家阅读完可以有所收获,同时也感谢各位铁汁们的支持。文章有任何问题可以在评论区留言,小羊一定认真修改,写出更好的文章~~

在这里插入图片描述

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

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

相关文章

【100天精通Python】Day72:Python可视化_一文掌握Seaborn库的使用《二》_分类数据可视化,线性模型和参数拟合的可视化,示例+代码

目录 1. 分类数据的可视化 1.1 类别散点图&#xff08;Categorical Scatter Plot&#xff09; 1.2 类别分布图&#xff08;Categorical Distribution Plot&#xff09; 1.3 类别估计图&#xff08;Categorical Estimate Plot&#xff09; 1.4 类别单变量图&#xff08;Cat…

远程IO:实现立体车库高效运营的秘密武器

随着城市的发展&#xff0c;车辆无处停放的问题变得越来越突出。为了解决这个问题&#xff0c;立体车库应运而生。立体车库具有立体空间利用率高、存取车方便、安全可靠等优点&#xff0c;成为现代城市停车的重要解决方案。 立体车库控制系统介绍 在立体车库中&#xff0c;控制…

基于51单片机的四种波形信号发生器仿真设计(仿真+程序源码+设计说明书+讲解视频)

本设计 基于51单片机信号发生器仿真设计 &#xff08;仿真程序源码设计说明书讲解视频&#xff09; 仿真原版本&#xff1a;proteus 7.8 程序编译器&#xff1a;keil 4/keil 5 编程语言&#xff1a;C语言 设计编号&#xff1a;S0015 这里写目录标题 基于51单片机信号发生…

简单明了!网关Gateway路由配置filters实现路径重写及对应正则表达式的解析

问题背景&#xff1a; 前端需要发送一个这样的请求&#xff0c;但出现404 首先解析请求的变化&#xff1a; http://www.51xuecheng.cn/api/checkcode/pic 1.请求先打在nginx&#xff0c;www.51xuecheng.cn/api/checkcode/pic部分匹配到了之后会转发给网关进行处理变成localho…

Android底层摸索改BUG(二):Android系统移除预置APP

首先我先提供以下博主博文&#xff0c;对相关知识点可以提供理解、解决、思考的 Android 系统如何预装第三方应用以及常见问题汇集android Android.mk属性说明及预置系统app操作说明系Android 中去除系统原生apk的方法 取消预置APK方法一&#xff1a; 其实就是上面的链接3&a…

Day 4 登录页及路由 (二) -- Vue状态管理

状态管理 之前的实现中&#xff0c;判断登录状态用了伪实现&#xff0c;实际当中&#xff0c;应该是以缓存中的数据为依据来进行的。这就涉及到了应用程序中的状态管理。在Vue中&#xff0c;状态管理之前是Vuex&#xff0c;现在则是推荐使用Pinia&#xff0c;在脚手架项目创建…

linux查看系统版本、内核信息、操作系统类型版本

1. 使用 uname 命令&#xff1a;这将显示完整的内核版本信息&#xff0c;包括内核版本号、主机名、操作系统类型等。 uname -a2. 使用 lsb_release 命令&#xff08;仅适用于支持 LSB&#xff08;Linux Standard Base&#xff09;的发行版&#xff09;&#xff1a;这将显示包含…

HCIE怎么系统性学习?这份HCIE学习路线帮你解决

华为认证体系覆盖ICT行业十一个技术领域共十三个技术方向的认证&#xff0c;今天我们分享的是其中最热门的数据通信方向的HCIE学习路线。 HCIE是华为认证体系中最高级别的ICT技术认证 &#xff0c;旨在打造高含金量的专家级认证&#xff0c;为技术融合背景下的ICT产业提供新的能…

JVS-BI数字大屏设计器:一站式解决方案

数字大屏介绍 数字大屏是当下数据展示、业务监控、指挥调度常见的业务表达形态&#xff0c;常有可视化的图表、效果装饰、事件操作等技术组成酷炫的效果展示。 配置入口 进入JVS-BI&#xff08;bi.bctools.cn&#xff09;&#xff0c;进入大屏页面&#xff0c;如下图所示 ①…

TypeScript之函数以及与JavaScript函数的区别

一、是什么 函数是JavaScript 应用程序的基础&#xff0c;帮助我们实现抽象层、模拟类、信息隐藏和模块 在TypeScript 里&#xff0c;虽然已经支持类、命名空间和模块&#xff0c;但函数仍然是主要定义行为的方式&#xff0c;TypeScript 为 JavaScript 函数添加了额外的功能&…

Docker 部署spring-boot项目(超详细 包括Docker详解、Docker常用指令整理等)

文章目录 DockerDocker的定义Docker有哪些作用Docker有哪些好处使用docker部署springboot项目安装docker创建Dockerfile镜像文件执行镜像文件(Dockerfile文件)查看Docker镜像启动容器查看Docker中运行的容器查看服务容器日志 Docker常用指令查看docker安装目录启动Docker停止Do…

无品牌国产PLC模块调试说明

地址30001对应的aiw9 30002对应aiw10 30003 aiw11 30004 aiw12 模块接线及拨码全部向下&#xff0c;对应的DeviceID为15地址 使用串口线链接的时候a要接b0 b接a0 要反着接才能有数据

金属压铸件自动化3D全尺寸测量设备自动外观检测三维检测-CASAIM

铸造作为现代装备制造工业的基础共性技术之一&#xff0c;铸件产品既是工业制造产品&#xff0c;也是大型机械的重要组成部分&#xff0c;被广泛运用在航空航天、工业船舶、机械电子和交通运输等行业。 铸件形状复杂&#xff0c;一般的三坐标或者卡尺圆规等工具难以获取多特征…

论文阅读——BART

Arxiv: https://arxiv.org/abs/1910.13461 一个去噪自编码器的预训练序列到序列的模型。是一个结合了双向和自回归transformers的模型。 预训练分为两个阶段&#xff1a;任意噪声函数破坏文本和序列模型重建原始文本 一、模型 input&#xff1a;被破坏的文本-->bidirecti…

【开发日记】必须记录一下困扰我两天的问题 MyBatisPlus适配达梦insert时提示:无效的列

【需求】 项目ORM框架使用的是MyBatisPlus&#xff0c;数据库原来使用的是MySQL&#xff0c;现在需要适配达梦。 【问题】 项目ORM框架使用的是MyBatisPlus&#xff0c;数据库原来使用的是MySQL&#xff0c;现在需要适配达梦数据库。 在适配过程中查询、更新、删除都没有问题…

购物车死了吗?拼多多的社交电商革命

亲爱的小伙伴们&#xff0c;大家好&#xff01;我是小米&#xff0c;今天要和大家聊一聊一个备受关注的话题&#xff1a;拼多多为什么没有购物车&#xff1f;这是一个网易产品经理面试题&#xff0c;但也是一个备受争议的话题。让我们一起来探讨一下吧&#xff01; 拼多多的购…

【idea】生成banner.txt

Spring Boot banner在线生成工具&#xff0c;制作下载英文banner.txt&#xff0c;修改替换banner.txt文字实现自定义&#xff0c;个性化启动banner-bootschool.netSpring Boot banner工具实现在线生成banner&#xff0c;轻松修改替换实现自定义banner&#xff0c;让banner.txt文…

Qt QWidget、QDialog、QMainWindow的区别

QWidget QWidget是Qt框架中最基础的窗口类&#xff0c;可以理解为用户界面的最基本单元。QWidget类提供了一个空白窗口&#xff0c;可以通过继承该类来创建自定义的窗口类。QWidget类提供了基本的窗口属性和方法&#xff0c;如大小、位置、标题、图标等。 QDialog QDialog是…

基于计算机视觉的 Transformer 研究进展

论文地址&#xff1a; https://kns.cnki.net/kcms/detail/11.2127.tp.20211129.1135.004.html 18页&#xff0c;74篇参考文献 目录 摘 要 1 Transformer 基本原理 1.1 编码器-解码器 1.2 自注意力 1.3 多头注意力 2 在计算机视觉领域的应用 2.1 图像分类 2.1.1 iGPT …