数据结构-时间复杂度和空间复杂度

news2024/12/27 10:10:20

时间复杂度和空间复杂度

    • 算法效率
    • 时间复杂度
    • 空间复杂度
    • 表示方法(大O的线性表示)
    • 举例说明
    • 时间复杂度举例说明
    • 空间复杂度举例说明
    • 冒泡排序的时间和空间复杂度
    • 递归情况下的时间和空间复杂度
    • 两个例子

算法效率

算法(Algorithn)是指用来操作数据,解决程序问题的一组方法。对于同一个问题使用不同的算法,结果或许相同,但所消耗的资源和时间会有所不同。这里通过时间和空间二个维度去考虑算法效率。

时间复杂度

一个算法所花费的时间与语句的执行次数成正比例,一个算法中语句的执行次数称为时间频度,用T(n)表示,n称为问题的规模。假设某个辅助函数 f ( n ) f(n) fn,当n趋近于无穷大时, T ( n ) / f ( n ) T(n)/f(n) Tn/fn的极限值为不为零的常数,记作 T ( n ) = O ( f ( n )) T(n)=O(f(n)) Tn=Ofn)),称 O ( f ( n )) O(f(n)) Ofn))为算法的渐进时间复杂度,简称为时间复杂度。

空间复杂度

空间复杂度是一个算法在运行过程中临时占用存储空间大小的一个量度。空间复杂度算的是变量的个数,包括数组,还有动态内存函数开辟出来的空间。

随着计算机行业的发展,计算机的存储容量已经达到了很高的程度,所以,我们更多关注的是时间复杂度。

表示方法(大O的线性表示)

规则:
1 用常数1取代所有加法常量,即所有常数项的执行的复杂度为 O ( 1 ) O(1) O1
2 在运行次数的函数中,只保留最高阶
3 如果最高阶项存在且不是1,则去掉与这个项目相乘的常数,得到的结果就是大O阶

值得注意的是:
一些算法存在最好,最坏和平均的情况。
例如在一个长度为N(这里的N是一个未知量)的数组中去搜索一个数据x,最好的情况:1次;最坏的情况:N次,平均:N/2次。

在实际中我们只关注算法的最坏运行情况作为时间复杂度或者空间复杂度。

举例说明

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

上述代码中,++count1语句被执行了 N 2 N^2 N2次,++count2执行了 2 ∗ N 2*N 2N次,++count3被执行了10次,还包括int count = 0;int M = 10;等有限常数个语句,可以看出最高阶项为 ‘ N 2 ‘ `N^2` N2,所以时间复杂度为 O ( N 2 ) O(N^2) ON2

时间复杂度举例说明

1

// 计算Func2的时间复杂度?
void Func2(int N)
{
	int count = 0;
	for (int k = 0; k < 2 * N; ++k)
	{
		++count1;
	}
 
	int M = 10;
	while (M--)
	{
		++count2;
	}
 
	printf("%d\n", count);
}

++count1;语句被执行2*N次,++count2;语句被执行次数为10次,还有其它有限的常数语句被执行,取其最高阶2N,根据大O规则,时间复杂度为O(N)


2

void Func4(int N)
{
	int count = 0;
	for (int k = 0; k < 100; ++k)
	{
		++count;
	}
	printf("%d\n", count);
}

++count的执行次数为100次,还有其余常量个语句,根据规则,其时间复杂度为O(1)


3

void Func(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);
}

上述的M,N都是未知量,所以时间复杂度为O(M+N)。如果题目有条件:如果M>>N,那么可以认为该复杂度为O(M),如果M<<N,那么可以认为该复杂度为O(N),M和N差不多大,既可以认为是O(M),也可以认为是O(N)


4


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

str函数用于查找字符串中的一个字符,并返回它在字符串的位置。这里我们默认数组或者字符串的长度为N,查找一个字符,最好的情况是一个就能找到,最坏的情况是查找N次。根据规则,时间复杂度取其最坏的情况,为O(N)


5
二分查找法的时间复杂度

int BinarySearch(int* a,int n,int x)
{
	assert(a);
	int begin = 0;
	int end = n - 1;
	
	while (begin<=end)
	{
		int mid=begin+((end-begin)>>1)//相当于除以2
			if (a[mid]<x)
			{
				begin = mid + 1;
			}
			else if (a[mid]>x)
			{
				end = mid - 1;
			}
			else
			{
				return a[mid];
			}

	}
	return -1;
}

最好的情况,查找一次就可以找到,最坏的情况可以如下分析:
我们需要从N份中拿出一份,如下图所示

在这里插入图片描述
假设查找次数为x,则N/2/2…=1, X = l o g 2 N X=log_2N X=log2N,所以其时间复杂度为O( l o g 2 N log_2N log2N),有时也会写成logN或者lgN。


空间复杂度举例说明

1

int i = 1;
int j = 2;
++i;
j++;
int m = i + j;

上述只创建了i,j,m,3个变量,为有限个空间,所以空间复杂度为O(1)


2

int arr[n]= {0}
for(i=1; i<=n; ++i)
{
   j = i;
   j++;
}

上述一个数组arr有n个变量,占用空间大小为n,还有一个变量i占用为1,根据规则,其空间复杂度为O(n)


冒泡排序的时间和空间复杂度

void BubbleSort(int* a,int n)
{
	for (int j=0;j<n;j++)
	{
		for (int i = 1; i < n - j; i++)
		{
			int exchange = 0;
			if (a[i-1]>a[i])
			{
				Swap(a + i - 1, a + i);
				exchange = 1;
			}
		}
		if (0==exchange)
		{
			break;
		}
	}
}

可以观察到int exchange = 0;,Swap(a + i - 1, a + i);,exchange = 1;这3个语句的执行量级为n^2,为最高阶项,所以时间复杂度为O( n 2 n^2 n2).其定义的变量有exchange,i,j,包括Swap函数中的临时变量temp,为有限个变量,所以其空间复杂度为O(1)


递归情况下的时间和空间复杂度

1

// 计算阶乘递归Factorial的时间复杂度?
long long Factorial(size_t N) 
{ 
	return N < 2 ? N : Factorial(N-1)*N;
 }

如果N<2,成立,则返回N,否则返回 F a c t o r i a l ( N − 1 ) ∗ N Factorial(N-1)*N Factorial(N1)N;
最好的情况是N<2成立,则返回N;最坏的情况函数一直调用

Factorial(N-1)
Factorial(N-2)
Factorial(N-3)
......
Factorial(3)
Factorial(2)
Factorial(1)

当调用到Factorial(1),函数再回归。这种情况下时间复杂度考虑的是函数的调用次数,为N次,所以时间复杂度为O(N)。每一次函数调用都会开辟栈帧,所以这里开辟的空间也是N,空间复杂度为O(N)


2

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

如果N<2,成立,则返回N,否则返回Fibonacci(N-1)+Fibonacci(N-2);
最好的情况是N<2成立,则返回N;最坏的情况函数一直调用
在这里插入图片描述
可以算出函数的总共调用次数为 2 0 + 2 1 + . . . + 2 n − 1 2^0+2^1+...+2^{n-1} 20+21+...+2n1,其量级为 2 n 2^n 2n,所以时间复杂度为O(2^N).空间复杂度,共需要开辟N次函数栈帧,所以空间复杂度为O(N)
一般情况下递归调用的空间复杂度为其调用的深度

两个例子

在这里插入图片描述
对于上述,如果依次拿着0到n的数字对比,那么需要两层for循环,它的时间复杂度就为O(n^2),就无法在O(n)的时间内完成。
为此,我们可以考虑以下两种方法:

1 异或。根据两个相同的数字异或为0,并且异或具有交换率,因此可以将0-n内的数字和缺少一个的0-n的数字异或,其结果就是缺少的值。

    异或,将0-n内的数字与缺少一个数字的0-n异或

     int val=0;
    for(int i=0;i<numsSize;i++)
    {
       val^=nums[i];

    }

     for(int i=0;i<=numsSize;i++)
     {
          val^=i;

     }

     return val;

它的时间复杂度为O(n)
2 直接套用公式,1-n的数字加和是一定的。

  // numSize默认为缺失后的数组的大小,正常情况下数组大小为numSize+1
    int tollal=((numsSize+1)*(0+numsSize))/2;

    for(int i=0;i<numsSize;i++)
    {
        tollal=tollal-nums[i];
    }
    return tollal;

它的时间复杂度也为O(n)
在这里插入图片描述
思路1:整体反转,需要轮转的前k个元素反转,其余的全部元素反转。代码如下:

void swap(int* m,int* n)
{
    int temp=*m;
     *m=*n;
     *n=temp;

}
void reverse(int* p,int length)
{
    int* left=p;
    int *right=p+length-1;
    while(left<right)
    {
        //交换值
        swap(left,right);
        left++;
        right--;
    }
}
    k=k%numsSize;
    reverse(nums,numsSize);
    reverse(nums,k);
    reverse(nums+k,numsSize-k);

时间复杂度是O(n)。整体逆置就是需要进行n/2次操作,前n-k次逆置和后k次逆置,也是需要进行n/2次操作。都是n的数量级
空间复杂度是O(1)。我们看到变量的创建都是有限个。


思路2:利用memcpy函数整体改变数组的顺序。

    k=k%numsSize;
    int* temp=(int*)malloc(sizeof(int)*numsSize);
    memcpy(temp,nums+numsSize-k,sizeof(int)*(k));
    memcpy(temp+k,nums,sizeof(int)*(numsSize-k));
    memcpy(nums,temp,sizeof(int)*(numsSize));
    free(temp);
    temp=NULL;

时间复杂度为O(n),虽然表面看起来,是有限次的语句,但是,memcpy内部也是一个一个进行copy的,需要进行n的数量级的次数
空间复杂度为O(n),开辟了n个空间。

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

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

相关文章

Spring MVC的核心类和注解

DispatcherServlet DispatcherServlet作用 DispatcherServlet是Spring MVC的核心类&#xff0c;也是Spring MVC的流程控制中心&#xff0c;也称为Spring MVC的前端控制器&#xff0c;它可以拦截客户端的请求。拦截客户端请求之后&#xff0c;DispatcherServlet会根据具体规则…

chatgpt赋能Python-python3接口自动化

Python3接口自动化&#xff1a;提升测试效率的利器 Python是一种高级编程语言&#xff0c;广泛应用于Web开发、数据科学、机器学习等领域。近年来&#xff0c;Python在接口自动化测试领域也变得越来越受欢迎。 Python的易读性、可扩展性以及模块化的特性&#xff0c;使得它成为…

Elasticsearch环境搭建(Windows)

一、介绍 布式、RESTful 风格的搜索和分析。 Elasticsearch 是位于 Elastic Stack 核心的分布式搜索和分析引擎。Logstash 和 Beats 有助于收集、聚合和丰富您的数据并将其存储在 Elasticsearch 中。Kibana 使您能够以交互方式探索、可视化和分享对数据的见解&#xff0c;并管…

手撕code(1)—— 排序算法

文章目录 前言1 冒泡排序2 选择排序3 插入排序4 快速排序5 归并排序6 堆排序7 希尔排序 前言 算法动画 时间复杂度分析 从小到大排序 1 冒泡排序 被动的将最大值送到最右边 1、比较相邻的元素。如果第一个比第二个大&#xff0c;就交换他们两个。 2、对每一对相邻元素作同…

Vite+Vue+iClient for Leaflet引入并实现MapV/Eharts第三方可视化库示例

作者&#xff1a;gaogy 文章目录 背景一、使用Vite构建Vue3JavaScript项目二、搭建iClient for Leaflet开发环境三、第三方可视化库Echarts的使用四、第三方可视化库MapV的使用五、其他地图库 背景 最近很多小伙伴咨询关于基于Vue3框架搭建iClent开发环境并使用Echarts与MapV等…

OPEN AI角色插件通道开放接入支持各种细分领域对话场景模型一键接入AI 智能

相信还是有很多伙伴不了解OPEN AI平台 &#xff0c;这里在细说一下 大家知道ChatGPT, 或者百度文心一言 阿里通意千问 包括各种其他的AI 聊天或者画图&#xff0c;等应用层出不穷。 但是我们要自己实现自己 语言大模型&#xff0c;或者说是人工智能应用能不能。 有实力当然可…

C++小项目之文本编辑器mynote(1.0.0版本)

2023年5月19日&#xff0c;周五晚上&#xff1a; 今天晚上突然想写一个运行在命令行上的文本编辑器&#xff0c;因为平时写文本时老是要创建新的文本文件&#xff0c;觉得太麻烦了。捣鼓了一个晚上&#xff0c;才选出一个我觉得比较满意的。我把这个程序添加到了系统环境变量中…

C语言指针学习笔记

1-二维数组指针 int a[3][4]a代表二维数组首元素的地址&#xff0c;此首元素不是一个简单的整形元素&#xff0c;而是由4个整形元素组成的一维数组&#xff0c;因此a代表的是首行&#xff08;序号为0的行&#xff09;的起始地址。a1代表序号为1的行的起始地址。a指向a[0], …

一个月50场面试,跑的慢就抢在别人前面!

300万字&#xff01;全网最全大数据学习面试社区等你来&#xff01; 今天的主人公也是一个应届生新人拿到满意offer的案例。 下面是一些聊天记录和面经&#xff0c;这名同学做的非常好的一个点&#xff0c;他把个人项目中的所用到的技术栈和项目具体的业务流程图以及用到的技术…

2年再见面

我和张哥是在两年前吃过饭&#xff0c;那时候我是在大学城上班。 两年前&#xff0c;张哥在微信上跟我说话&#xff0c;说要来深圳找我&#xff0c;问我什么时间方便&#xff0c;请我吃个便饭。两年前&#xff0c;公众号还比较火热。有挺多人找我做事情&#xff0c;找我做事情之…

桥梁安全监测,智能化桥梁结构健康监测方案

桥梁是现代城市交通网络中不可或缺的组成部分&#xff0c;但由于长期受到自然环境和人为因素的影响&#xff0c;桥梁的安全问题一直备受关注。传统的桥梁检测方式主要是靠人力进行巡查&#xff0c;这种方式效率低下、成本高&#xff0c;而且难以全面掌握桥梁结构的真实情况。随…

回顾 | Let's Learn .NET-通过 Semantic Kernel .NET SDK 管理你的 OpenAI 项目

点击蓝字 关注我们 编辑&#xff1a;Alan Wang 排版&#xff1a;Rani Sun Lets Learn .NET 系列 “Lets Learn .NET” 是面向全球的 .NET 初学者学习系列&#xff0c;旨在通过不同语言&#xff0c;帮助不同地区的开发者掌握最新的 .NET 开发知识与技能。 在 ChatGPT 与 OpenAI…

从零玩转设计模式之简单工厂设计模式-jiandangonchangmoshi

title: 从零玩转设计模式之简单工厂设计模式 date: 2022-12-08 11:31:19.472 updated: 2022-12-11 23:03:34.805 url: https://www.yby6.com/archives/jiandangonchangmoshi categories: - 设计模式 tags: - 设计模式 简单工厂模式是一种创建型设计模式&#xff0c;用于创建单…

Docker安装MinIO教程

本章教程&#xff0c;主要介绍一下&#xff0c;如何在Linux用Docker安装MinIO。 MinIO是一个高性能、分布式对象存储系统&#xff0c;支持S3 API&#xff0c;适用于云原生环境。MinIO可以在标准硬件上运行&#xff0c;并且具有低延迟、高吞吐量、高可用性和可扩展性等优势。Min…

C语言两百行代码实现简易扫雷

文章目录 前言一.代码实现二.设计思路main()函数搭建框架reset ( )函数dis_play( )函数setmine( )函数player_move( )函数 前言 扫雷应该是我们接触到的第一个电脑游戏&#xff0c;用c语言实现扫雷对初学者来说是一个不错的锻炼 编写扫雷只需要用到数组、函数和生成随机数的知…

Java文件与IO流

首先我们要清楚什么是流&#xff0c;正如其名&#xff0c;很形象&#xff0c;流就是像水一样的东西&#xff0c;具有方向性&#xff0c;在java中 &#xff0c;流大概就是类 接下来&#xff0c;我们要对输入输出流有一个基本认识&#xff0c;什么是输入输出流呢&#xff1f; 输入…

漏斗分析、 python学习路径地图、数据科学技能书知识地图、数据安全治理解决方案、AIGC发展研究、经营的本质…| 本周精华...

▲点击上方卡片关注我&#xff0c;回复“8”&#xff0c;加入数据分析领地&#xff0c;一起学习数据分析&#xff0c;持续更新数据分析学习路径相关资料~&#xff08;精彩数据观点、学习资料、数据课程分享、读书会、分享会等你一起来乘风破浪~&#xff09;回复“小飞象”&…

【计算机组成原理】(四)原码补码的加减乘除

各种码的作用&#xff1a; 模运算的性质&#xff1a; -3&#xff08;-1&#xff09;*129 90*129 211*129 332*129 -15&#xff08;-2&#xff09;*129 我们发现等号右边都是9&#xff0c;相当于等号的左边的数除去12的余数都是9 那我们就说这几个等好左边的数&#xff0…

从零玩转设计模式之原型模式-yuanxingmoshi

title: 从零玩转设计模式之原型模式 date: 2022-12-11 20:05:35.488 updated: 2022-12-23 15:35:44.159 url: https://www.yby6.com/archives/yuanxingmoshi categories: - 设计模式 tags: - 设计模式 - 原型模式 什么是原型模式设计模式? 原型模式是一种软件设计模式&#…

【Rust 日报】2023-05-19 Bevy主分支添加了对WebGPU的支持

cascade-protocol Rust实现的 Cascade information reconciliation protocol for Quantum Key Distribution (QKD) 量子键分布&#xff1f;好像是密码学相关的。 隔行如隔山&#xff0c;小编也不懂这个具体是干啥的。感兴趣的同学可以前去研究。 https://github.com/gftea/casc…