时间复杂度和空间复杂度的计算

news2024/12/23 6:08:58

目录

算法的复杂度

时间复杂的的概念

时间复杂度计算方法

大O的渐进表示法

空间复杂的概念

空间复杂的的计算方法

时间和空间复杂度的应用

消失的数字

轮转数组


算法的复杂度

 

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

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

时间复杂的的概念

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


即:找到某条基本语询与问题规模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 j = 0; j < N; ++j)
	{
		++count;
	}
	int M = 10;
	while (M--)
	{
		++count;
	}
	printf("%d\n", count);
}

 我们可以分开来求Func1中++count的执行次数,Func1中有三重循环 ,第一重循环为嵌套的for循环。

在第一重循环中 执行++count的次数为 N*N

在第二重循环中 执行++count的次数为 N

在第三重循环中 执行++count的次数为 M 次,其中 M=10

由此得出时间复杂度的函数式: F(N)=N^2+2*N+10

通过这个时间复杂度的函数式,我们发现当N越大,后两项对结果的影响是越小的:

 

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

大O的渐进表示法

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

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

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

 首先,根据上个例子,我们可以轻松得到Func2的精确时间复杂度的函数式:F(N)=2N+10

再根据大O的渐进表示法,去掉与这个项目相乘的常数2和加法常数10,最后得到它的空间复杂度为: 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);
}

这道题并没有说明M和N的大小关系,因此时间复杂度为 :O(M+N)

但是:

如果M远大于N ,我们可以认为时间复杂度就是 O(M);

如果M远大于N ,我们可以认为时间复杂度就是 O(N);

如果M和N的大小近似,我们可以认为时间复杂度是 O(N)或者是O(M);

特别提示:一般情况下时间复杂度计算时未知数都是用的N,但是也可以是M、K等等其他的

实例3:计算Func2的时间复杂度?

void Func4(int N)
{
	int count = 0;
	for (int k = 0; k < 100; ++k)
	{
		++count;
	}

	printf("%d\n", count);
}

 这个函数中并不是给我们一个未知数,而是直接给我们一个常数100,可能有的小伙伴会认为它的时间复杂度就是O(100), 其实并不是这样的。大O的渐进表示法第一条上写道:1、用常数1取代运行时间中的所有加法常数,  因此func4函数的时间复杂度是O(1)哦!

同时我们也要知道时间复杂度O(1) 并不是说只能进行一次运算,而是能运行常数次

实例4:计算strchr的时间复杂度?

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

 首先我们要了解strchr的大致内容:

根据最坏的运行结果,strchr的时间复杂度为O(N) (N为*str的长度)

实例5:计算Bubblesort的时间复杂度?

void Bubb1eSort(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=n时:   第二层for循坏执行 N-1 次

当end=n-1时:第二层for循坏执行 N-2 次

当end=n-2时:第二层for循坏执行 N-3 次

当end=n-3时:第二层for循坏执行 N-4 次

 ——

当end=2时:   第二层for循坏执行 1次

总的执行次数就是 1+2+3+4+..+N=N*(N-1)/2

 因此Bubblesort的时间复杂度为 O(N^2)

这个实例间接表明:算时间复杂度不能只看是几层循环,而要看它的思想

实例6:计算BinarySearch的时间复杂度?

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

计算二分查找的时间复杂度也是看它的最坏情况:

开始时要查找的范围是N,每次查找一次,范围就缩小一倍,即N/2/2/2/....(N>=0)

因此二分查找的时间复杂度是 O(log 2 N)

实例7:计算阶乘递归Fac的时间复杂度?

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

 解释起来有点麻烦,不如直接看图:

 

 

空间复杂的概念

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

空间复杂的的计算方法

实例1:计算Bubblesort的空间复杂度?

​void Bubb1eSort(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;
	}
}

在函数内就只定义了两个变量——endi

虽然每经过一次循环end和i都会被销毁,但是当循环再次开始时,再次定义 end 和 它们还是使用原来那块空间,所以函数额外使用的空间数是2.

所以本题的空间复杂度为:O(1)

实例2:计算Fibonacci的空间复杂度?

返回斐波那契数列的前n项

1ong 1ong * Fibonacci(size_t n)
{
	if (n == 0)
		return NULL;

	1ong 1ong * fibArray = (1ong 1ong*)ma11oc((n + 1) * sizeof(1ong 1ong));
	fibArray[0] = 0;
	fibArray[1] = 1;
	for (inti = 2; i <= n; ++i)
	{
		fibArray[i] = fibArray[i - 1] + fibArray[i - 2];
	}

	return fibArray;
}

 很明显,空间复杂度是O(N), 因为开了一个n+1的数组

 顺便计算它的时间复杂度:O(N)  函数内就只有一层for循环

实例3:计算阶乘递归Fac的空间复杂度?

1ong 1ong Fac(size_t N)
{
	if (N == 1)
		return 1;

	return Fac(N - 1) * N;
}

每次递归都要建立栈帧,每个栈帧创建常数个变量,递归n次就建立了n个栈帧,因此空间复杂度是 O(N)

递归函数的空间复杂的与递归的深度有关

时间和空间复杂度的应用

消失的数字

原题链接:力扣

 思路:

 方法一:先对数组中的元素进行排序,需要用到qsort快排,时间复杂度为O(n*log2N)

 方法二:先求出前n项和sum=(1+n)*n/2, 然后要求的数x=sum-(a[0]+a[1]+a[2]+...+a[n-1]) ,  时间复杂度是O(N),空间复杂度是O(1)

方法三:创建长度n的数组,原数组中值是几就在第几个位置写一下这个值,然后遍历数组,找出没有值的那一项,时间复杂度是O(N),空间复杂度是O(N)

方法四:给一个值x=0,先让x与0~n进行异或,再让x与数组中的每一个值进行异或,最后x就是要求的数, 时间复杂度是O(N)

使用方法四来解题:

int missingNumber(int* nums, int numsSize)
{
    int x=0;
    //跟[0,n]异或
    for(int i=1;i<=numsSize;i++)
    {
        x^=i;
    }
    //在跟数组中值异或
    for(int i=0;i<numsSize;i++)
    {
        x^=nums[i];
    }
    return x;
}


 

轮转数组

原题链接:力扣

 方法一暴力求解,旋转k次 时间复杂度是O(N*K),空间复杂度是O(1)

方法二:开辟额外空间 以空间换时间  时间复杂度是O(N),空间复杂度是O(N)

 

 方法三:首先将前n-k个逆置,再将后k个逆置,最后整体逆置, 时间复杂度是O(N),空间复杂度是O(1)  

 

用方法三来解题:

void reverse(int*nums,int left,int right)
{
    while(left<right)
    {
        int tem=nums[left];
        nums[left]=nums[right];
        nums[right]=tem;
        ++left;
        --right;
    }
} 

void rotate(int*nums,int numsSize,int k)
{
    if(k>=numsSize) k%=numsSize;
    //前n-k个数逆置
    reverse(nums,0,numsSize-k-1);
    //后k个逆置
    reverse(nums,numsSize-k,numsSize-1);
    //整体逆置
    reverse(nums,0,numsSize-1);
}

全文结束就结束啦~~

 

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

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

相关文章

modbus转profinet网关连接5台台达ME300变频器案例

通过兴达易控Modbus转Profinet&#xff08;XD-MDPN100&#xff09;网关改善网络场景&#xff0c;变频器有掉线或数据丢失报警&#xff0c;影响系统的正常运行&#xff0c;将5台 ME300变频器modbus转Profinet到1200PLC&#xff0c;通过网关还可以实现Profinet转modbus RTU协议转…

LabVIEW中以编程方式获取VI克隆名称

LabVIEW中以编程方式获取VI克隆名称演示如何以编程方式获取VI的名称或克隆名称。如果VI作为顶级VI运行&#xff0c;则将显示VI的名称。如果VI在主VI中用作子VI&#xff0c;它将返回克隆的名称。在项目开发过程中&#xff0c;有时需要获取VI的名称。在此示例中&#xff0c;实现了…

【数论】试除法判断质数,分解质因数,筛质数

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 现已更新完KMP算法、排序模板&#xff0c;之…

代码管理--svnadmin工具介绍

1、简介 SVNAdmin2 是一款通过图形界面管理服务端SVN的web程序。正常情况下配置SVN仓库的人员权限需要登录到服务器手动修改 authz 和 passwd 两个文件&#xff0c;当仓库结构和人员权限上了规模后&#xff0c;手动管理就变的非常容易出错&#xff0c;本系统能够识别人员和权限…

【AWS入门】IAM基本应用-2023/3/4

目录IAM概述根用户和IAM用户参考IAM概述 IAM(Identity Access Management&#xff09;是身份和访问管理服务&#xff0c;要访问AWS服务和资源&#xff0c;就要使用IAM进行身份验证和授权。当我们通过控制台&#xff0c;CLI&#xff0c;或API访问AWS服务时&#xff0c;都需要通…

p5.js map映射

theme: smartblue 本文正在参加「金石计划」 本文简介 带尬猴&#xff0c;我嗨德育处主任 p5.js 为开发者提供了很多有用的方法&#xff0c;这些方法实现起来可能不难&#xff0c;但却非常实用&#xff0c;能大大减少我们的开发时间。 本文将通过举例说明的方式来讲解 映射 map…

《计算机网络》期末复习笔记

文章目录一、一些英文名词的标签&#xff08;方便记忆&#xff09;二、OSI七层协议三、综合题3.0 知识点储备3.1 在Internet 网中&#xff0c;某计算机的IP 地址是11001010.01100000.00101100.01011000 &#xff0c;请回答下列问题3.2 假定发送方要发送的数据为10000101。采用C…

【Spring 深入学习】AOP的前世今生之后续

AOP的前世今生之后续 1. 概述 上篇文章【Spring 深入学习】AOP的前世今生之代理模式我们讲述了代理模式。而我们今天的主人公AOP就是基于代理模式实现的&#xff0c;所以我们今天会简单学习下AOP 2. 什么是AOP 是面向切面编程&#xff0c;一般可以帮助我们在不修改现有代码的情…

Java中字符流(FileReader(read、close)、FileWriter(write、close)、字符(输入、输出)流原理解析)

1.创建对象&#xff1a; 2.读取数据 3.释放资源&#xff08;关流&#xff09; 如何使用重载的read()方法呢&#xff1f; FileWriter&#xff1a; 在前面我们指导&#xff0c;字节输出流和字符输出流的本质区别是&#xff0c;字节输出流一次只能操作一个字节&#xff0c;如果让…

QEMU启动ARM32 Linux内核

目录前言前置知识ARM Versatile Express开发板简介ARM处理器家族简介安装qemu-system-arm安装交叉编译工具交叉编译ARM32 Linux内核交叉编译ARM32 Busybox使用busybox制作initramfs使用QEMU启动ARM32 Linux内核模拟vexpress-a9开发板模拟vexpress-a15开发板参考前言 本文介绍采…

【数据库】数据库基础架构

数据库架构 数据库对于后端程序员来说是每天都需要打交道的系统&#xff0c;因此了解并掌握MySQL底层原理是必须的。 基础架构图 MySQL内部分为两层&#xff0c;一个是Server层&#xff0c;另一个是存储引擎层&#xff0c;而我们常用的就是MyISAM、InnoDB&#xff0c;主要负…

16、字符串生成器

目录 &#xff08;1&#xff09;append()方法 &#xff08;2&#xff09;insert(int offset, arg)方法 &#xff08;3&#xff09;delete(int start , int end)方法 创建成功的字符串对象&#xff0c;其长度是固定的&#xff0c;内容不能被改变和编译。虽然使用“”可以达到…

Java【二叉搜索树和哈希表】详细图解 / 模拟实现 + 【Map和Set】常用方法介绍

文章目录前言一、二叉搜索树1、什么是二叉搜索树2、模拟实现二叉搜索树2.1, 查找2.2, 插入2.3, 删除3、性能分析二、模型三、哈希表1、什么是哈希表1.1, 什么是哈希冲突1.2, 避免, 解决哈希冲突1.2.1, 避免: 调节负载因子1.2.2, 解决1: 闭散列(了解)1.2.3, 解决2: 开散列/哈希桶…

Uipath Excel 自动化系列18-RefreshPivotTable(刷新透视表)

活动描述 RefreshPivotTable(刷新透视表)&#xff1a;如果透视表的数据源发生变化&#xff0c;需使用刷新透视表活动&#xff0c;该活动需与Use Excel File 活动选择的 Excel 文件一起使用。 使用如下图&#xff1a; RefreshPivotTable(刷新透视表)属性 属性 作用 Display…

最近做到一道好题,特来和大家分享一下。

题目&#xff1a;299. 裁剪序列 - AcWing题库 分析&#xff1a; 题目给我们的数据范围是105;也就是说我们的时间复杂度要控制到O(nlog2n)才可以。假设每一个元素都可以作为一个区间&#xff0c;那么可以有 Cn1……Cnn 2n-1种划分方法&#xff0c;n达到了105,很显然就超时。所以…

MASK-RCNN网络介绍

目录前言一.MASK R-CNN网络1.1.RoIPool和RoIAlign1.2.MASK分支二.损失函数三.Mask分支预测前言 在介绍MASK R-CNN之前&#xff0c;建议先看下FPN网络&#xff0c;Faster-CNN和FCN的介绍&#xff1a;下面附上链接&#xff1a; R-CNN、Fast RCNN和Faster RCNN网络介绍FCN网络介绍…

调试射频TX和rx实验工程出现的问题与反思

1.今天用ADS仿真 发现 加上SMA 插损就到了4db&#xff0c;但是直接用传输线就在1db以内 这个问题我目前想到的排查思路是换成IPEX&#xff0c; 换成IPEX插损就变成2db 拿最新的7626去看 看到上面是SMA-3G 小针 还是结合参考的demo PCB来看 2.用射频的ipex测试LNA 发现校准…

Leetcode. 160相交链表

文章目录指针解法指针解法 核心思路 &#xff1a; 先 分别求两个链表的长度 然后长的链表先走 差距步&#xff08;长-短&#xff09; 最后长链表和短链表同时走 &#xff0c;第一地址相同的就是交点 &#xff0c;注意一定是地址相同 不可能出现上图这种情况 &#xff0c;因为C1…

开放平台订单接口

custom-自定义API操作 ​ ​​注册开通​​ taobao.custom 公共参数 名称 类型 必须 描述 key String 是 调用key&#xff08;必须以GET方式拼接在URL中&#xff09; secret String 是 调用密钥 api_name String 是 API接口名称&#xff08;包括在请求地址中&a…

【JavaSE专栏11】Java的 if 条件语句

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;Java全栈软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云…