时间复杂度与空间复杂度

news2024/11/20 7:21:29

文章目录

  • 1.什么是数据结构
  • 2.什么是算法
  • 3.如何学好数据结构呢
    • 3.1写代码
    • 3.2 多去动手画图
  • 4.算法效率
    • 4.1如何评判一个算法的好与坏呢
    • 4.2算法的复杂度
  • 5.时间复杂度
    • 5.1 概念
    • 5.2大O渐进法
  • 6常见的时间复杂度
    • 6.1常数阶
    • 6.2线性阶
    • 6.3 对数阶
    • 6.4平方阶
    • 6.5函数调用
      • 6.5.1普通调用
      • 6.5.2递归调用
    • 6.6不确定循环次数
    • 6.7时间复杂度的比较
  • 7.空间复杂度

先给大家一段话:

If you give someone a program , you will frustrate them for a day ; if you teach them how to program , you will frustrate them for a lifetime

如果你交给某人一个程序,你将折磨他一整天;如果你教某人如何编程,你将折磨他一辈子!!!

今天我们来看看数据结构!!!!

1.什么是数据结构

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

数据元素的集合。

在这里插入图片描述

从这幅图我们也看出数据结构的重要性了吧。

2.什么是算法

算法(Algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为

输出。简单来说算法就是一系列的计算步骤,将数据转换成输出结果

3.如何学好数据结构呢

3.1写代码

在学习数据结构时,你觉得听起来挺简单,但是如果你一动手就不会了,所以我们要多用手去敲代码,把同一个结构敲上十几次
在这里插入图片描述

敲到这种程度就好😄😄

3.2 多去动手画图

在遇到比较难理解的结构与算法,我们要去拿纸去画一画它是如何实现的。

眼过千遍不如手过一遍

在这里插入图片描述

4.算法效率

4.1如何评判一个算法的好与坏呢

求前100个整数相加的和

小王:我写了一个代码,很好。

#include<stdio.h>
int main()
{
	int i = 0;
	int sum = 0;
	int n = 100;

	for (i = 0; i < n; i++)
	{
		sum = sum + i;
	}

	printf("%d\n", sum);
	return 0;
}

小田:我也写了一个,比你的好

#include<stdio.h>
int main()
{
	int n = 100;
	int sum = n * (n - 1) / 2;

	printf("%d\n", sum);
	return 0;
}

那么他们两个到底谁的方法好呢?如何评判的呢?

当然是有评判标准的!!!!

4.2算法的复杂度

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

是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。

时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算

机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计

算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。

5.时间复杂度

5.1 概念

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

一 个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。

但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。

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

切记:时间复杂度是基本语句执行的次数多少,而不是运行的时间。

如果用运行时间来判断,由于机器性能的不同,相同代码跑出来的时间是不同的。所以不用运行时间来比较。

先看个例子吧

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

求一下++count语句一共执行了多少次。

答: N*N + 2*N+ 10

这就是他的基本语句执行的次数,也就是它的时间复杂度

Func1 执行的基本操作次数 :

N*N + 2*N+ 10

N = 10 F(N) = 130

N = 100 F(N) = 10210

N = 1000 F(N) = 1002010

N*N
N = 10 F(N) = 100
N = 100 F(N) =10000
N = 1000 F(N) = 1000000

我们可以看出,随着N的增大,只有N*N对这个结果影响比较大,而2*N+10随着N的增大,影响基本没有。

所以,实际上我们计算时间复杂度是,并不需要像我们上面那样精确的计算,而只需要计算大概的执行次数。

什么是大概的执行次数呢?

有一个大O渐进法为我们规定了。

5.2大O渐进法

推导大O阶:

1、用常数1取代时间中的所有加法常数

2、在修改后的运行次数函数中,只保留最高阶项

3、如果最高级项存在且其系数不是1,则去除与这个项相乘的系数

用这个三个规则得到的就是大O阶

6常见的时间复杂度

6.1常数阶

#include<stdio.h>
int main()
{
	int sum = 0;                       //执行一次
	int n = 100;                       //执行一次
	sum = (1 + n) * n / 2;             //执行一次
	printf("%d", sum);                 //执行一次

	return 0;                          //执行一次
}

上面代码一共执行5次

时间复杂度是O(5)吗?

不是,根据大O渐进法 用常数1取代时间中的所有加法常数

只要是常数相加,就看作1,你哪怕是相加为1000000,也看作1。

所以上面代码时间复杂度:O(1)

6.2线性阶

#include<stdio.h>
int main()
{
	int i = 0;                           //执行一次
	int n = 10;                         //执行一次
	for (i = 0; i < n; i++)             //执行N次
	{
		//时间复杂度为O(1)的程序步骤
	}

	return 0;                            //执行一次
}

这个时间复杂度为:O(N)

如果细算的话 F(N) = N+3

根据大O渐进法,第二条在修改后的运行次数函数中,只保留最高阶项

最高阶为: N

6.3 对数阶

₂₂₂#include<stdio.h>
int main()
{
	int count = 1;
	int n = 128;
	while (count < n)
	{
		count = count * 2;
	}

	return 0;
}

由于每次count乘以2,就离N越来越近了

2^x = N;

x = log₂N

但是习惯上将logNlog₂N等价

所以这个程序的时间复杂度:O(logN)

二分法的时间复杂度就是:O(logN)

6.4平方阶

#include<stdio.h>
int main()
{
	int n = 100;
	for (int i = 0; i < n; i++)
	{
		for (int j = 0; j < n; j++)
		{
			//时间复杂度为O(1)的程序
		}
	}

	return 0;
}

这是一个循环嵌套

外层每循环1次,内层循环N次

外层循环N次,内层循环N次

一共执行多少条语句呢?

F(N) = N + N +…+N N个N相加

则时间复杂度:O(N*N)

在看一个程序

#include<stdio.h>
int main()
{
	int n = 100;
	for (int i = 0; i < n; i++)
	{
		for (int j = i; j < n; j++)   //注意:j = i
		{
			//时间复杂度为O(1)的程序
		}
	}

	return 0;
}

i = 0时,内层循环执行了n次

i= 1时,内层循环执行了n-1次

.

.

i= n-1时,内层循环执行了1次

一共执行:F(N) = N+(N-1) +(N-2) +…+1 = N*N/2+N/2

取最高阶 N*N/2

在根据大O渐进法,第三条:如果最高级项存在且其系数不是1,则去除与这个项相乘的系数

将1/2变成1

则时间复杂度为:O(N*N)

6.5函数调用

6.5.1普通调用

#include<stdio.h>
void fundtion(int n)
{
	printf("%d ", n);
}

int main()
{
	int i = 0;
	int j = 0;
	int n = 10;
	for (i = 0; i < n; i++)
	{
		function(i);
	}
	return 0;
}

for循环内,调用了函数,这个函数内的时间复杂度是1。

则这个程序的时间复杂度就是:O(N) 由for循环决定

#include<stdio.h>
void fundtion(int n)
{
   int j = 0;
   for (j = 0; j < n; j++)
   {
   	//时间复杂度为O(1)的程序步骤
   }
}
int main()
{
   int i = 0;
   int j = 0;
   int n = 10;
   for (i = 0; i < n; i++)
   {
   	function(i);
   }
   return 0;
}

时间复杂度为:O(N*N)

==我们可以将函数中的实现,带到for循环中,==这个和上面刚刚说的是一样的,你们可以算算。

6.5.2递归调用

long long Fac(size_t N) 
{		
	if (0 == N)
		return 1;

	return Fac(N - 1) * N;
}

这是一个递归,它的时间复杂度又是多少呢?

在这个函数中,基本语句是两条。它一共递归了N次

则总共执行:F(N) = 2*N

则时间复杂度是:O(N)

6.6不确定循环次数

二分查找

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);
		if (a[mid] < x)
			begin = mid + 1;
		else if (a[mid] > x)
			end = mid - 1;
		else
			return mid;
	}
	return -1;
}

这种我们不确定它需要循环错少次才可以找到,那么时间复杂度是多少呢?

假设有N个数字,我们需要在这N个数字中找一个数字

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

如果循环一次就找到了,那时间复杂度:O(1)

如果在最后一次找到了,那么时间复杂度:O(logN)

每次查找,范围缩小一半,如果最后一次才找到

1*2*2*2…*2 == N

2^x = N (x为最大循环的次数)

x = logN

最坏的结果就是,循环了logN次,最后一次找到

那么这种情况下,我们如何确定这个程序的时间复杂度呢?

举个例子:

如果有一天,你要和你女盆友去吃饭,但是,你在当天有点事情,这个事情最早是下午四点就完了,最晚是下午五点完成,你该给你女盆友说几点去吃饭呢?

你说四点,万一你没有到,你女盆友等了你一个小时,你终于来了,她也不想听你解释了

你女盆友说:你自己去吃吧,咱两分手吧

这不就很难受,就因为一句话

你如果你说五点,结果你早早就结束了,你等了她一个小时

你女盆友说:哇,你真好,真有心

最终你们就走在了一起

就算最坏的结果你五点才到,那也没什么说的本来就是五点,你们还是可以开开心心吃饭。

一样的,我们一般计算不确定循环次数的时间复杂度,按最坏的打算算

所以二分查找的时间复杂度:O(logN)最坏的打算

6.7时间复杂度的比较

在这里插入图片描述

从下向上时间复杂度以此增大。

7.空间复杂度

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

空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。

空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法**。

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

注:

空间复杂度,用的也是大O渐进法

空间复杂度是看变量的个数,只要是在程序运行起来需要的额外空间

第一个例子

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

看看这个函数的空间复杂度,我们一般将形式参数,不算在这个函数的空间复杂度中。

size_t end 是一个变量 size_t = unsigned int

int exchange 是一个变量

size_t i 是一个变量

所以说这个函数一共有三个变量,根据大O渐进法

空间复杂度:O(1)

第二个例子

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

long long* fibArray 开辟了N+1个空间,相当于N+1个变量

int i 是一个变量

根据大O渐进法,空间复杂度:O(N)

第三个例子

long long Fac(size_t N) 
{
	if (N == 0)
		return 1;

	return Fac(N - 1) * N;
}

这个递归函数的空间复杂度又是多少呢?

有人说:这里面没有变量,是不是O(0)

其实不是的

函数调用,会在栈上开辟栈帧空间(想成函数调用会开辟空间就好)

把开辟的这个空间就当成一个变量,即一个函数的空间O(1)

那么一共调用了N次,一共开辟了,N个栈帧空间

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;

}


> long long* fibArray 开辟了N+1个空间,相当于N+1个变量
>
> int i 是一个变量

根据大O渐进法,空间复杂度:O(N)

==第三个例子==

```c
long long Fac(size_t N) 
{
	if (N == 0)
		return 1;

	return Fac(N - 1) * N;
}

这个递归函数的空间复杂度又是多少呢?

有人说:这里面没有变量,是不是O(0)

其实不是的

函数调用,会在栈上开辟栈帧空间(想成函数调用会开辟空间就好)

把开辟的这个空间就当成一个变量,即一个函数的空间O(1)

那么一共调用了N次,一共开辟了,N个栈帧空间

则空间复杂度:O(N)

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

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

相关文章

1024程序节|Android框架之一 BRVAH【BaseRecyclerViewAdapterHelper】使用demo

文章目录&#x1f353;&#x1f353;BRVAH 上部&#x1f344;&#x1f353;动态图结果展示&#x1f344;&#x1f344;myAdapter.java【第一个布局适配器】&#x1f344;&#x1f344;youAdapter.java【第二个布局适配器】&#x1f344;&#x1f344;MainActivity.java【主活动…

【Android】自制静音App,解决他人手机外放问题

契源 看到一个粉丝留言&#xff0c;吐槽舍友深夜手机外放&#xff0c;打扰别人休息&#xff0c;想设计一款软件阻止舍友行径。于是我就来简单设计一下。 需求实现分析 实际上&#xff0c;我之前有篇博文提到过一个类似的Android APP&#xff0c;主要功能是将手机声音强制开到…

内存函数 memcpy、memmove 的简单模拟实现

一、memcpy 函数 数memcpy从source的位置开始向后复制num个字节的数据到destination的内存位置。注意是以字节为单位进行拷贝。函数声明如下&#xff1a; 1、参数返回值解析 第二个参数 src&#xff1a;源地址&#xff0c;即你要从哪开始拷贝。 第三个参数 count&#xff1a…

Qt 物联网系统界面开发 “ 2022湖南省大学生物联网应用创新设计竞赛技能赛 ——应用物联网的共享电动自行车 ”

文章目录前言一、实现效果二、程序设计1. 界面背景图设计2. 信号槽设计3. 定时器设计4. 动态曲/折线图的设计5. 摄像头扫码6. 注册设计7. 登录设计8. 巡检人员设计三、综合分析前言 本篇源于 “ 2022 湖南省大学生物联网应用创新设计竞赛技能赛参考样题 ” ——应用物联网的共享…

【git】git ssh 公钥私钥 在 windows和mac 双系统分别如何生成 以及对接各个平台说明

win和mac 双系统分别如何生成 git ssh 一、windows 生成 ssh 公钥私钥 windows版本需要下载git bash&#xff1a;https://gitforwindows.org/ 在 git bash 中输入如下指令&#xff1a; # 创建全局名称&#xff08;将会在你的git提交作者中显示&#xff09;git config --glo…

【allegro 17.4软件操作保姆级教程三】布局操作基础二

4精准定位与坐标定位 在设计中经常会有一些器件或结构孔要摆放在指定位置&#xff0c;如果用move命令用鼠标去移则很难定位完全&#xff0c;这时候就需要精准定位。 操作步骤为&#xff1a; 1、点击move命令&#xff0c;在option面板选择器件原点&#xff0c;这时器件就会悬停在…

策略分析中缺失值的处理方法

在日常的策略分析中&#xff0c;经常会碰到分析的变量出现缺失值的情况&#xff0c;如果对这些缺失值视而不见&#xff0c;则会对策略分析的结果造成一定的影响。那么我们如何处理缺失值呢&#xff1f;关注“金科应用研院”&#xff0c;回复“CSDN”领取“风控资料合集” 首先…

本地数据库IndexedDB - 学员管理系统之登录(一)

IndexedDB是浏览器提供的本地数据库&#xff0c;它可以被网页脚本创建和操作。IndexedDB允许存储大量数据&#xff0c;提供查找接口&#xff0c;还能建立索引。这些都是LocalStorage或Cookie不具备的。就数据库类型而言&#xff0c;IndexedDB不属于关系型数据库&#xff08;不支…

插入排序图解

七大排序之插入排序 文章目录七大排序之插入排序前言一、直接插入排序1.1 算法图解1.2 算法稳定性1.3 插入排序和选择排序相比到底优在哪&#xff1f;二、折半插入排序总结前言 博主个人社区&#xff1a;开发与算法学习社区 博主个人主页&#xff1a;Killing Vibe的博客 欢迎大…

springboot:实现文件上传下载实时进度条功能【附带源码】

0. 引言 记得刚入行的时候&#xff0c;做了一个文件上传的功能&#xff0c;因为上传时间较久&#xff0c;为了用户友好性&#xff0c;想要添加一个实时进度条&#xff0c;显示进度。奈何当时技术有限&#xff0c;查了许久也没用找到解决方案&#xff0c;最后不了了之。 近来偶…

全网最全面的pytest测试框架进阶-conftest文件重写采集和运行测试用例的hook函数

【文章末尾有.......】 使用pytest不仅仅局限于进行单元测试&#xff0c;作为底层模块可扩展性强&#xff0c;有必要理解其运行机制&#xff0c;便于进行二次开发扩展&#xff0c;通过文档的学习很容易理解。 构建一个简单的测试脚本 import pytest import requestsdef add(…

Hive数据倾斜常见场景及解决方案(超全!!!)

Hive数据倾斜常见问题和解决方案 文章目录 前言、一、Explain二、数据倾斜&#xff08;常见优化&#xff09;前言 Hive数据倾斜是面试中常问的问题&#xff0c;这里我们需要很熟练地能举出常见的数据倾斜的例子并且给出解决方案。 一、Explain 我们可以通过sql语句前面加expa…

公众号网课查题搭建方法

公众号网课查题搭建方法 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff08;…

QLC 闪存给主控带来了很大的难题?

前言 世界各大主流闪存厂商&#xff0c;如美光、海力士、铠侠和长江存储积极致力于QLC的研发&#xff0c;并相继推出了QLC SSD 产品。随着技术的不断进步&#xff0c;人们普遍担心的QLC擦写寿命少正逐渐被改善。QLC SSD 成本是最大的优势&#xff0c;不指望说替代 TLC SSD&…

408 | 【2011年】计算机统考真题 自用回顾知识点整理

选择题 T3&#xff1a;循环队列 不同指针指向&#xff0c;队列判空/判满条件 1. rear:指向队尾元素 front:指向队头元素前一个位置 &#xff08;1&#xff09;牺牲一个存储空间 &#xff08;2&#xff09;判空条件&#xff1a;front rear &#xff08;3&#xff0…

【RHCSA】管理Linux的联网

目录 rhel8与旧版本的区别 NetworkManager的特点 配置网络 (1)使用P命令配置临时生效的网络连接 (2)修改配置文件&#xff0c;前提是需要有network服务[不推荐] (3)nmcli(命令行工具) 网络测试命令 Ⅰ、使用ping命令测试网络的连通性 Ⅱ、使用tracepath命令跟踪并显示网…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java危险品运输车辆信息管理系统b2z1o

大学毕业设计&#xff0c;一般都是自己或者几个同学一起弄&#xff0c;lunwen都是去&#xff0c;百度&#xff0c;图书馆找很多资料参考&#xff0c;&#xff08;就是把里面都了&#xff0c;自己再按照各个意思重新表达&#xff09;&#xff0c;但是前提&#xff0c;提纲要想好…

【附源码】计算机毕业设计SSM微课程服务系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

神经网络芯片的单片机,什么是神经网络芯片

1、神经网络做图像分类一定要用到gpu吗&#xff1f; GPU最大的价值一直是“accelerating”(加速)&#xff0c;GPU不是取代CPU&#xff0c;而是利用GPU的并行计算架构&#xff0c;来将并行计算的负载放到GPU上来处理从而极大的提升处理速度。GPU本质上在异构计算架构上属于协处…

Hello Word你真的理解了么?今天教我的表弟,有些感悟

&#x1f36c;博主介绍 &#x1f468;‍&#x1f393; 博主主页&#xff1a;喵的主页 ✨主攻领域&#xff1a;【大数据】【java】【python】【面试分析】 Hello world1. 编写程序2. 打开命令行3. 运行 .class 文件4. 排查错误1. 编写程序 是不是都忘了我们初学时是打开记事本的…