数据结构初阶--绪论

news2024/11/25 19:28:33

目录

一.什么是数据结构

二.什么是算法

三.算法的时间复杂度

四.算法的空间复杂度

五.复杂度练习

题一:消失的数字

题二:旋转数组


一.什么是数据结构

数据结构:是相互之间存在一种或多种特定关系的数据元素的集合。

数据结构的三要素:

二.什么是算法

程序=数据结构+算法。数据结构研究如何把现实世界的问题信息化,将信息存进计算机。同时还要实现对数据结构的基本操作。算法研究如何处理这些信息,解决实际问题。

三.算法的时间复杂度

算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一个算法所花费的时间与其中语句的执行次数成正比,算法中的基本操作的执行次数,为算法的时间复杂度。

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

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

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

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

  1. 最坏情况:任意输入规模的最大运行次数(上界);
  2. 平均情况:任意输入规模的期望运行次数;
  3. 最好情况:任意输入规模的最小运行次数(下界)。

但是,在实际中一般关注的是算法的最坏运行情况。

案例一

void Func1(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(2N + 10)

采用大O渐进法得时间复杂度为:O(N)

案例二:

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

分析:

总体时间复杂度为:O(N+M)

采用大O渐进法得时间复杂度为:O(N+M)

案例三:

void Func3(int N) 
{
 	int count = 0;

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

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

分析:

总体时间复杂度为:O(100)

采用大O渐进法得时间复杂度为:O(1)

案例四:

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

分析:

strchr函数是一个字符查找函数,用以实现在字符串str中查找字符character。

最好的情况:第一个元素即为要查找的元素,时间复杂度为O(1)。最坏的情况:最后一个元素才为要查找的元素或者遍历整个字符串之后查找失败,时间复杂度为O(N)。而时间复杂度一般看的是最坏情况,因此总体时间复杂度为:O(N)

采用大O渐进法得时间复杂度为:O(N)

案例五:

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

}	

分析:

冒泡排序:比较相邻的两个元素,数值小的元素慢慢的向前移动,数值大的元素慢慢的向后移动。

最好的情况:整个数组是有序排列的,只需要比较n-1次,而不需要移动元素,此时的时间复杂度为:O(N)。最坏的情况:整个数组是由大到小排列的,总共需要n-1轮循环,则第一轮循环需要比较n-1次,第二轮循环需要比较n-2次,第三轮循环需要比较n-3次,...,第n-1轮循环需要比较1次。则总共比较的次数为(n-1)*n/2次。而时间复杂度一般看的是最坏情况,因此总体时间复杂度为:O((N - 1)*N/2)

采用大O渐进法得时间复杂度为:O(N^2)

案例六:

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;
 		else
 			return mid;
 	}

 	return -1;
}

分析:

二分查找法:二分查找法是一种高效的查找算法,用于在有序数组或有序列表中快速定位目标元素的位置。它通过将目标值与数组中间元素进行比较,从而将查找范围缩小一半,不断迭代直到找到目标元素或确定目标元素不存在。

最好的情况:查找一次变查找到所要查找的元素,此时的时间复杂度为O(1)。最坏的情况:当数组中只剩下最后一个元素,或者查找失败,由于二分查找法每次范围都是缩小一半,则:n / 2 / 2 / 2… /2 = 1。假设查找x次,则2^x=n,两边取对数得:x=log2^n,所以总共比较的次数为次log2^n。而时间复杂度一般看的是最坏情况,因此总体时间复杂度为:O(log2^N)

采用大O渐进法得时间复杂度为:O(log2^N)

案例七:

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

 	return Fac(N-1)*N; 
}

分析:

在计算阶乘递归Fac时,可以发现函数只递归调用了N-1次。

总体时间复杂度为:O(N-1)

采用大O渐进法得时间复杂度为:O(N)

案例八:

long long Fib(size_t N) 
{
 	if(N < 3)
 		return 1;

 	return Fib(N-1) + Fib(N-2);
}

分析:

在计算斐波那契递归Fib时,可以发现函数递归呈现指数式增长。

将2^0+2^1+2^2+2^3+…2^(N-2)进行累加得:2^(N-1) - 1,此数也即为函数递归调用的次数。

总体时间复杂度为:O(2^(N-1) - 1)

采用大O渐进法得时间复杂度为:O(2^N)

四.算法的空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂度算的是变量的个数。它的计算规则基本和时间复杂度类似,也使用大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;
 	}

}	

分析:

需要注意的是,函数形参中的数组a,和大小n是不需要占用空间的,因为函数运行时所需要的栈空间(存储参数,局部变量,一些寄存器信息等)在编译期间已经确定好了。在函数中,新增的变量其实是end和i,以及exchange。

总体空间复杂度为:O(3)

采用大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;
 }

分析:

该函数返回的是斐波那契数列的前n项,为了存放该数列的前n项,需要调用malloc函数来动态开辟n+1个空间来存放。

总体空间复杂度为:O(n+1)

采用大O渐进法得空间复杂度为:O(N)

案例三:

long long Fib(size_t N) 
{
 	if(N < 3)
 		return 1;

 	return Fib(N-1) + Fib(N-2);
}

分析:

在解答此题之前需要明白的一点是:时间是累计的,空间是不累计的,可以重复利用。在绝大多数情况下,函数递归调用时的空间复杂度=递归调用的深度。

Fib(N-1)和Fib(N-2)在递归的过程中,存在很多相同的部分,在这些相同的部分,它们是共用同一块内存空间的。

采用大O渐进法得空间复杂度为:O(N)

案例四:

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

	return Fac(N-1)*N;
}

分析:

在绝大多数情况下,函数递归调用时的空间复杂度=递归调用的深度。该函数递归调用了N次,开辟了N个栈帧,每个栈帧使用了常量个空间。

采用大O渐进法得空间复杂度为:O(N)

五.复杂度练习

题一:消失的数字

描述:

数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?

分析:

法一:
使用malloc开辟一个大小为N+1的数组,并初始化为-1。然后遍历数组nums中的各个数字,这个数字是多少就写到新开辟的数组的对应的位置上去,最后再遍历一遍新开辟的数组,哪个位置是-1,这个位置的下标就是缺失的数字。                                                                                                              时间复杂度为:O(N),空间复杂度为:O(N)。

法二:
用异或的思想。设x=0,将x跟数组中的这些数据都异或一遍,然后再跟0-N之间的数字异或一遍,最后得到的x就是缺失的数字                                                                                                                        时间复杂度为:O(N)
注意:一个数与自身异或,总是为0,一个数与0异或,总是其自身。

实现:

int missingNumber(int* nums, int numsSize)
{
	int x = 0;

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

	for (int j = 0; j < numsSize + 1; j++)
	{
		x ^= j;
	}

	return x;
}

法三:

排序+二分查找相结合。
排序算法的时间复杂度:冒泡O(N^2) ,快排O(N*logN)
二分查找的时间复杂度:O(log2^N)

法四:

使用求和公式。如果有n个数,则0+1+2…+n,最后整体再减去数组中的值的累加就是缺失的数。                                                                                                                                                             时间复杂度:O(N)

实现:

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

	return sum;
}

题二:旋转数组

描述:

给你一个数组,将数组中的元素向右轮转k个位置,其中k是非负数。使用时间复杂度为O(N),空间复杂度为O(1)的原地算法解决这个问题。

分析:

法一:

设置变量tmp。将最右边的元素拷贝到tmp中,然后将数组中的值依次右移动一位,再把tmp中的内容存放到数组起始位置;重复上述操作k次,就可以实现数组的旋转。                                               时间复杂度为O(N^2),空间复杂度为O(1)。

法二:

以空间换时间。首先开辟一个新数组,用以存放原数组中的元素。然后将原数组中的后k个元素保持起始序列依次存放到新数组的开始k个位置。其次将原数组剩余的n - k个元素保持起始序列依次存放到新数组的后n-k个位置。最后再将新数组的元素依次拷贝到原数组中。                                        时间复杂度为O(N),空间复杂度为O(N)。

实现:

void rotate(int* nums, int numsSize, int k)
{
	int n = numsSize;
	int tmp[n];//变长数组【C99】

	//首先对k做一个取模操作,防止数组访问越界
	k %= n;

	//将后k个数字移动到前面
	int j = 0;
	for (int i = n - k; i < n; ++i)
	{
		tmp[j++] = nums[i];
	}

	//将前n - k个数字移动到后面
	for (int i = 0; i < n - k; ++i)
	{
		tmp[j++] = nums[i];
	}

	//将移动完的数组再拷贝回原数组
	for (int z = 0; z < n; ++z)
	{
		nums[z] = tmp[z];
	}
		
}

法三:

三步翻转法。首先将前n-k个元素进行逆置,然后将后k个元素进行逆置,最后再将n个元素进行整体逆置。                                                                                                                                                           时间复杂度为O(N),空间复杂度为O(1)。

实现:

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

void rotate(int* nums, int numsSize, int k)
{
	k %= numsSize;
	reverse(nums, numsSize - k, numsSize - 1);
	reverse(nums, 0, numsSize - k - 1);
	reverse(nums, 0, numsSize - 1);
}

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

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

相关文章

【LittleXi】 N-gram模型(C++实现)

LittleXi N-gram模型&#xff08;C实现&#xff09;马尔科夫性 (独立性假设)代码实现英文训练版本中文训练版本 训练效果 N-gram模型&#xff08;C实现&#xff09; 定义&#xff1a;通俗地讲&#xff0c;就是利用前文的单词&#xff0c;来推算下一个最大概率出现的单词 马尔…

Web入门-Web服务器

Web服务器是一个程序软件&#xff0c;对HTTP协议的操作进行封装&#xff0c;使得程序员不必直接对程序进行操作&#xff0c;让Web开发更加便捷&#xff0c;简化web程序开发。主要功能是“通过网上信息浏览服务”。 Tomcat 概念&#xff1a;Tomcat是阿Apache软件基金会一个核心…

Coggle 30 Days of ML(23年7月)任务八:训练BILSTM模型

Coggle 30 Days of ML&#xff08;23年7月&#xff09;任务八&#xff1a;训练BILSTM模型 任务八&#xff1a;使用Word2Vec词向量&#xff0c;搭建BILSTM模型进行训练和预测 说明&#xff1a;在这个任务中&#xff0c;你将使用Word2Vec词向量&#xff0c;搭建BILSTM模型进行文…

【雕爷学编程】Arduino动手做(158)---VL53L0X激光测距模块3

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

从零开始制作一个Web蜜罐扫描器(2)

从零开始制作一个Web蜜罐扫描器(0)_luozhonghua2000的博客-CSDN博客 从零开始制作一个Web蜜罐扫描器(1)_luozhonghua2000的博客-CSDN博客 文件读取和写入实现 上面的工作已经完成了逻辑判断的部分,下面还需要进一步完善一些旁支末节的部分因为爬虫生成的文件是一个ison文件…

熵权法 —— matlab

目录 一、熵权法介绍 二、熵权法赋权步骤 1.指标正向化 mapminmax介绍 2.数据标准化 3.计算信息熵 4.计算权重以及得分 三、实例分析 1.读取数据 2.指标正向化 2.1 越小越优型处理 2.2 某点最优型指标处理 3.数据标准化 4.计算信息熵 5.计算权重 6.计算得分 总…

第三章 技术选型

1、需要考虑以下几点 可控性 必须有人可以兜底&#xff0c;可以解决 稳定性 修改的版本最好可以上下兼容 核心项目不要用最新的技术 commit的时长 是否有团队维护 适用性 更多人都会的技术 易用性 学习曲线 2、有哪些好用的移动端React组件库 3、为什么使用NestJS 1、最像…

【LeetCode: 16. 最接近的三数之和 | 双指针专题 】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

港联证券|涨停不封板意味着什么?

涨停不封板或许是因为股票价格到达了涨停价&#xff0c;可是买卖量还未到达涨停板规则的要求&#xff0c;因而买卖并没有被暂停。涨停不封板也有或许是个股当天涨停之后&#xff0c;又呈现开板的状况。 总归&#xff0c;涨停不封板意味着上方的抛压较多&#xff0c;空方力气大于…

Node.js开发

Node.js是一个基于V8 JavaScript引擎的JavaScript运行时环境。 也就是说Node.js基于V8引擎来执行JavaScript的代码&#xff0c;但是不仅仅只有V8引擎&#xff1a;  前面我们知道V8可以嵌入到任何C 应用程序中&#xff0c;无论是Chrome还是Node.js&#xff0c;事实上都是嵌入…

C++智能指针使用及原理

在讲解之前&#xff0c;先讲述一种RAII思想. 目录 RAII 智能指针原理 auto_ptr auto_ptr的介绍 auto_ptr的实现 unique_ptr unique_ptr的介绍 unique_ptr实现 shared_ptr shared_ptr的介绍 shared_ptr实现 weak_ptr weak_ptr的介绍 weak_ptr的实现 RAII RAII&a…

西安大华时代网络科技有限公司

大华时代是行业领先的软件与信息技术服务企业。我们拥有专业的团队和先进的技术&#xff0c;在金融、电信、高科技和互联网等行业&#xff0c;为客户提供优质的产品、先进的解决方案和热情的服务&#xff01;

【SpringBoot + Mybatis系列】插件机制 Interceptor

【SpringBoot Mybatis系列】插件机制 Interceptor 在 Mybatis 中&#xff0c;插件机制提供了非常强大的扩展能力&#xff0c;在 sql 最终执行之前&#xff0c;提供了四个拦截点&#xff0c;支持不同场景的功能扩展 Executor (update, query, flushStatements, commit, rollbac…

mysql check slow_log造成锁-waiting for table level lock

背景&#xff1a; 我们在生产环境使用mysql的时候开启了slow_log 记录表&#xff0c;但有的时候由于记录数据过大&#xff0c;造成该表损坏 ERROR 1194 (HY000): Table slow_log is marked as crashed and should be repaired 这时候就想着用check table 命令来看看表是否正…

云服务器下WordPress发送邮件的设置

WordPress的邮件功能很强大&#xff0c;可以实现用户密码以往后自助恢复等问题。 WordPress默认是使用php发邮件的&#xff0c;php需要配置好smtp&#xff08;端口25&#xff09;服务器及密码。这种方式不直观&#xff0c;因此一般都用smtp插件&#xff0c;常用的插件是WP Mai…

FastDDS 源码剖析:src/cpp/fastdds 源码结构与Publisher源码分析

目录 源码结构 Publisher分析 Publisher 类分析 PublisherIImpl 类分析 源码结构 —builtin:该目录包含FastDDS使用的内置类型和协议的实现。 —core:该目录包含FastDDS库中使用的核心类和函数。这包括处理错误、管理内存和处理线程的类。 --domain:此目录包含DomainPart…

本地资源检测 自定义规则 零基础上手指南

本地资源检测是UWA推出的、面向于静态资源的全量分析。可以全面自动检测项目静态工程内各项资源、代码和设置&#xff0c;能够帮助项目组制定合理的资源与代码标准&#xff0c;及时发现潜在的性能问题和异常错误&#xff0c;建立有效的开发规范。其中“自定义规则”功能特别获得…

docker 安装zookeeper单机版

1. 安装版本3.5.7, 也可以自己去官网找到自己需要的版本复制命令即可 https://hub.docker.com/_/zookeeper/tags docker pull zookeeper:3.5.7 2. 创建映射文件夹&#xff1a; #1. 在centos中创建三个文件夹 mkdir -p /home/zookeeper/conf mkdir -p /home/zookeeper/data mkd…

图片视频抹除算法总结Inpaint

基本是从图片抹水印和视频抹水印两个方向 Video Inpainting&#xff1a;https://paperswithcode.com/task/video-inpaintingImage Inpainting&#xff1a;https://paperswithcode.com/task/image-inpainting 请根据目录查看 图片 Partial Conv 部分卷积层 源自于Image In…

游戏服务器搭建过程中Maven多模块编译遇到的一些问题

目录 1、多模块的创建 1.1 父模块的创建 1.2 删除垃圾文件 1.3 修改pom.xml 1.4 创建子模块继承 2、子模块之间的互相引用 3、多个模块间版本的管理 3.1 dependencis 3.2 dependencyManagement 4、依赖执行 5、在Spring Boot项目中加载依赖项目的组件有几种常用的方法…