首页 > 其他分享 >数据结构入门之复杂度

数据结构入门之复杂度

时间:2024-10-27 08:50:31浏览次数:3  
标签:count 入门 int 复杂度 ++ 时间 空间 数据结构

前言:终于来到了数据结构。要想学好数据结构,首先就要了解数据结构的复杂度。那么,什么是复杂度呢?

1 数据结构

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

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

那么如何来衡量一个算法的好坏呢?

2 复杂度

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

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

2.1 时间复杂度

定义:在计算机科学中,算法的时间复杂度是一个函数式T(N),它定量的描述了算法的运行时间。时间复杂度是衡量程序的时间效率,那为什么不去计算程序的运行时间呢?

原因有以下几点:

1.程序运行时间和编译环境,运行机器的配置都有关系。在同样的机器不同的编译器上运行时间也可能有所不同。

2.同一个算法程序在不同的机器上运行时间也会有所不同。

3.并且时间只能在程序写好后测试,不能写程序前通过理论思想计算评估。

那么函数式T(N)到底是什么呢?这个T(N)函数式计算的是程序的执行次数。假设计算机每条指令执行的时间基本一样(实际中有差别,但是差别不大),那么执行次数和运行时间成正相关。执行次数就可以代表程序时间效率的优劣。

#include<stdio.h>
#include<time.h>
int main()
{
	time_t ret1 = time(NULL);
	int arr[100000] = { 6,4,8,2,9,1,7,3,10,5 };
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (int i = 0; i < sz - 1; i++)
	{
		for (int j = 0; j < sz - 1 - i; j++)
		{
			if (arr[j] > arr[j + 1])
			{
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
			}
		}
	}
	time_t ret2 = time(NULL);
	printf("%lld\n", ret2 - ret1);
	return 0;
}

这段代码就很好的证明了上述观点。程序每次运行起来获取的时间不一定是一样的。接下来就是习题大集合。来看看下面代码的复杂度吧。

// 请计算⼀下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 k = 0; k < 2 * N; ++k)
	{
		++count;
	}
	int M = 10;
	while (M--)
	{
		++count;
	}
}

在这里插入图片描述

实际在计算时间复杂度时,计算的并不是程序执行的准确次数(这样做的意义也不大)。所以我们只需要计算程序执行的大概次数就可以了。复杂度的表示通常使用大O的渐进表示法

2.2 大O的渐进表示法

大O渐进表示法的规则

1.时间复杂度函数式T(N)中,只保留最高阶项,去掉低阶项。
因为N不断变大时,低阶项对结果影响越来越小,当N无穷大时,
就可以忽略不计了。
2.如果最高阶项存在且不是1,则去除这个项的系数。因为当N
不断变大时,这个系数对结果的影响越来越小,当N无穷大时,
就可以忽略不计了。
3.T(N)中如果没有N相关的项,只有常数项。则用常数1代替。

所以,上述Func1代码的时间复杂度是O(N^2)。

接下来的代码时间复杂度又是多少呢?

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

在这里插入图片描述

根据大O的渐进表示法,Func2的时间复杂度是O(N)。

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

在这里插入图片描述

Func3的时间复杂度分为三种情况:

1.M>>N,Func3的时间复杂度是O(M)

2.M<<N,Func3的时间复杂度是O(N)

3.M与N近似相等,Func3的时间复杂度是O(N)或O(M)

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

在这里插入图片描述

// 计算strchr的时间复杂度?
const char* strchr(const char* str, int character)
{
	const char* p_begin = s;
	while (*p_begin != character)
	{
		if (*p_begin == '\0')
		{
			return NULL;
		}
		p_begin++;
	}
	return p_begin;
}

在这里插入图片描述

总结:

最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
大O的渐进表示法在实际中一般情况关注的是算法的上界,也就是最坏运行情况
// 计算BubbleSort的时间复杂度?
void BubbleSort(int* a, int n)
{
	assert(a);
	for (size_t end = 0; end < n - 1; ++end)
	{
		int exchange = 0;
		for (size_t i = 0; i < end - i - 1; ++i)
		{
			if (a[i] > a[i + 1])
			{
				Swap(&a[i], &a[i + 1]);
				exchange = 1;
			}
		}
		if (exchange == 0)
			break;
	}
}

在这里插入图片描述

因此,BubbleSort的时间复杂度是O(N^2)。

void func5(int n)
{
	int cnt = 1;
	while (cnt < n)
	{
		cnt *= 2;
	}
}

在这里插入图片描述

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

在这里插入图片描述

2.3 空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行期间因为算法的需要额外临时开辟的空间。空间复杂度并不是程序占用多少个Bytes的空间,因为常规情况下每个对象的大小差异不会很大,所以空间复杂度计算的是变量的个数

空间复杂度也使用大O的渐进表示法。

注意函数运行时所需要的栈空间在编译期间就已经确定好了,因此空间复杂度主要通过函数在运行时显示申请的额外空间来确定

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

BubbleSort函数所需要的栈空间在编译期间就已经确定好了,只需要关注函数在运行时所需要申请的额外空间。BubbleSort额外申请的空间有end,exchange等有限个局部变量,使用了常数个空间,因此BubbleSort空间复杂度是O(1)

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

Fac递归调用了N次,开辟了N个栈帧空间,每个栈帧使用了常数个空间,因此空间复杂度为O(N)。

3 常见复杂度对比

在这里插入图片描述

标签:count,入门,int,复杂度,++,时间,空间,数据结构
From: https://blog.csdn.net/2402_84532723/article/details/143165894

相关文章

  • 【Python入门】7天速成Python网络爬虫高手,Autoscraper从零基础到实战只需一篇
    ......
  • (神经网络和卷积入门)Pytorch小土堆跟练代码(第8天)
    本系列为跟练小土堆每集代码,然后进入李宏毅机器学习教程。在系列中会敲完所有视频中代码,并且在注释详细写出感悟和易错点。欢迎大家一起交流!最前面的神经网络和卷积,可以移步我的另一个帖子池化层只提取一部分特征,可以大大的加快训练速度最后输出类似于马赛克的效果'池......
  • ElasticSearch 入门需要了解的概念
    引言:ElasticSearch的定位与应用ElasticSearch是一个分布式搜索和分析引擎。想象它是一个超大的图书馆:可以快速找到任何书籍(搜索能力)可以统计各类书籍的数量(分析能力)可以随时添加新书架(可扩展性)即使某个书架损坏,其他书架的书仍然可读(高可用性)主要应用场景:网站搜索日志......
  • RabbitMQ 入门(三)SpringAMQP消息转换器
    一、消息转换器Spring会把你发送的消息序列化为字节发送给MQ,接收消息的时候,还会把字节反序列化为Java对象。只不过,默认情况下Spring采用的序列化方式是JDK序列化。众所周知,JDK序列化存在下列问题:-数据体积过大-有安全漏洞-可读性差JDK序列化方......
  • 数据结构:(OJ917)仅仅反转字母
    给你一个字符串 s ,根据下述规则反转字符串:所有非英文字母保留在原有位置。所有英文字母(小写或大写)位置反转。返回反转后的 s 。示例1:输入:s="ab-cd"输出:"dc-ba"示例2:输入:s="a-bC-dEf-ghIj"输出:"j-Ih-gfE-dCba"示例3:输入:s="Test1ng-Leet=code-Q!"输出:"......
  • MybatisPlus入门(二)MybatisPlus入门案例
    一、SpringBoot整合MyBatisPlusSpringBoot整合MyBatisPlus入门案例:步骤一:创建新模块,选择Spring初始化,并配置模块相关基础信息。选择当前模块需要使用的技术集(仅保留JDBC)手动添加MyBatisPlus起步依赖:<dependency><groupId>com.baomidou</groupId><artifactId>myb......
  • 数据结构之队列
    一、队列的定义队列是一种操作受限的线性表,队列只允许在表的一端进行插入,在表的另一端进行删除。可进行插入的一段称为队尾,可进行删除的一端称为队头。队列的主要特点就是先进先出。依照存储结构可分为:顺序队和链式队。二、顺序队列一开始front(队头)和rear(队尾)都在数......
  • MybatisPlus入门(一)MybatisPlus简介
    一、MyBatis简介MyBatisPlus(简称MP)是基于MyBatis框架基础上开发的增强型工具,旨在简化开发、提高效率-官网:https://mybatis.plus/  https://mp.baomidou.com/MyBatisPlus特性:-无侵入:只做增强不做改变,不会对现有工程产生影响-强大的CRUD操作:内置通用Mapper,少......
  • 第12题——入门级js
    题目网址:https://match.yuanrenxue.cn/match/12解题步骤看流量包和其回显数据。只有一个流量包,那就是只要访问该网址就能获取页面数据。看下请求地址的组成。变量m一看就是base64编码,解码看下原字符串。再尝试访问第二页,看看原字符串组成的规律。比较明了了,原字符串......
  • 【2024版】PyCharm专业版下载+安装+汉化教程,Pycharm环境配置和使用指南,零基础小白Pyth
    前言PyCharm是一款由JetBrains公司推出的PythonIDE。它提供了一个简单易用的图形用户界面,并且具有很多有用的功能,如代码补全和自动代码检查,帮助开发人员更加高效地编写Python代码。此外,PyCharm还提供了调试器和版本控制系统集成,使得开发人员能够更加轻松地管理和维护他们的......