探索数据结构:深入了解顺序表的奥秘

news2024/9/24 7:24:50


✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:数据结构与算法
贝蒂的主页:Betty’s blog

1. 什么是顺序表

顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,它与数组非常类似,但是相比于数组顺序表有一个非常明显的优点——可以动态内存增长空间大小

2. 顺序表的功能

顺序表可以大致包含以下几个功能:

  1. 初始化顺序表中的数据。

  2. 打印顺序表中的数据。

  3. 对顺序表进行尾插(末尾插入数据)。

  4. 对顺序表中数据进行尾删(末尾删除数据)。

3. 顺序表的定义

定义顺序表我们首先需要一个动态内存开辟的空间,当前数据的个数(size),以及方便扩容的容量大小(capacity)

typedef int SLDataType; //类型重命名,方便后续修改类型
#define N 4 //初始化空间大小
typedef struct SeqList
{
	SLDataType* a;    //指向动态开辟的数组
	size_t size;      //有效数据个数
	size_t capacity;  //容量大小
}SeqList;

4. 功能的具体实现

4.1 初始化顺序表

(1) 代码实现

初始化顺序表时我们可以先开辟四个空间,之后不够再进行扩容。

void SeqListInit(SeqList* p)
{
	assert(p);  //断言
	p->a =(SLDataType*)malloc(N*sizeof(SLDataType));  //初始顺序表为四个空间
	if (p == NULL)
	{
		perror("malloc fail:");
                return ;
	}
	p->size = 0;  //初始数据个数为0
	p->capacity = 4;  //初始空间容量为4
}
(2) 复杂度分析
  • 时间复杂度:由于没有其他未知数的引入,所以所需时间是个常数,也就是O(1)。
  • 空间复杂度:动态开辟N个空间,所以空间复杂度为O(N)。

4.2 打印数据

(1) 代码实现

打印数据只用循环打印就可以了。

void SeqListPrint(const SeqList* p)
{
	assert(p);  //断言
	if (p->size == 0)  //判断顺序表是否为空
	{
		printf("顺序表为空\n");
		return;
	}

	int i = 0;
	for (i = 0; i < p->size; i++)  //打印顺序表
	{
		printf("%d ", p->a[i]);
	}
	printf("\n");
}
(2) 复杂度分析
  • 时间复杂度:因为会打印顺序表中的所有数据,所以时间复杂度为O(N)。
  • 空间复杂度:打印顺序表并不需要开辟格外的空间,所以空间复杂度为O(N)。

4.3 尾插数据

尾插数据,顾名思义就是在顺序表末尾插入数据,在插入数据之前我们应该检查顺序表是否为满。

(1) 检查是否已满
void CheckCapacity(SeqList* p)
{
	assert(p);  //断言
	if (p->size == p->capacity)  //检查容量,满了则增容
	{
		size_t newcapacity = 2 * p->capacity;  //,扩容为原来的2倍
		SLDataType* prt = (SLDataType*)realloc(p->a, newcapacity * sizeof(SLDataType));  //扩容
		if (prt == NULL)
		{
			perror("realloc fail:");
			return ;
		}
		p->a = prt;  // prt不为空,开辟成功
		p->capacity = newcapacity;  //更新容量
	}
}
(2) 插入数据
void SeqListPushBack(SeqList* p, SLDataType x)
{
	assert(p);  //断言
	CheckCapacity(p);  //检查顺序表容量是否已满
	p->a[p->size] = x;  //尾插数据
	p->size++;  //有效数据个数+1
}
(3) 复杂度分析
  • 时间复杂度:没有变量影响时间,时间复杂度为O(1)。
  • 空间复杂度:以最坏的情况考虑,会进行扩容,空间复杂度为O(N)。

4.4 尾删数据

同理,尾删数据就是删除顺序表中最后一个元素,我们只需将size–。注意:删除之前要保证顺序表中有数据

(1) 代码实现
void SeqListPopBack(SeqList* p)
{
	assert(p);  //断言
	assert(p->size > 0);  //顺序表不能为空
	p->size--;  //有效数据个数-1
}
(2) 复杂度分析
  • 时间复杂度:没有变量影响时间,时间复杂度为O(1)。
  • 空间复杂度:没有变量影响空间,空间复杂度为O(N)。

4.5 头插数据

头部插入数据就是在第一个元素位置插入一个元素。

(1) 代码实现
void SeqListPushFront(SeqList* p, SLDataType x)
{
	assert(p);  //断言
	CheckCapacity(p);  //检查顺序表容量是否已满

	int i = 0;
	for (i = p->size - 1; i >= 0; i--)  //顺序表中[0,size-1]的元素依次向后挪动一位
	{
		p->a[i + 1] = p->a[i];
	}
	p->a[0] = x;  //头插数据
	p->size++;  //有效数据个数+1
}
(2) 复杂度分析
  • 时间复杂度:因为从头部插入数据,后续数据需要依次覆盖,所以时间复杂度为O(N)。
  • 空间复杂度:因为可能会进行扩容,按最坏的情况来算,空间复杂度为O(N)。

4.5 头删数据

从头部删除数据,与尾部删除数据不同,需要依次往前覆盖。

(1) 代码实现
void SeqListPopFront(SeqList* p)
{
	assert(p);  //断言
	assert(p->size > 0);  //顺序表不能为空

	int i = 0;
	for (i = 1; i < p->size; i++)  //顺序表中[1,size-1]的元素依次向前挪动一位
	{
		p->a[i - 1] = p->a[i];
	}
	p->size--;  //有效数据个数-1
}
(2) 复杂度分析
  • 时间复杂度:因为需要往前覆盖数据,所以时间复杂度自然为O(N)。
  • 空间复杂度:因为并没有开辟其他空间,所以空间复杂度为O(1)。

4.6 查找指定值

根据输入参数,在顺序表中查找指定的值并返回其下标,若未找到则返回-1。

(1) 代码实现
int SeqListFind(const SeqList* p, SLDataType x)
{
	assert(p);  //断言

	int i = 0;
	for (i = 0; i < p->size; i++)
	{
		if (p->a[i] == x)
		{
			return i;  //查找到,返回该值在数组中的下标
		}
	}
	return -1;  //没有查找到
}
(2) 复杂度分析
  • 时间复杂度:以最坏的情况分析,时间复杂度为O(N)。
  • 空间复杂度:并没有格外的空间消耗,空间复杂度为O(1)。

4.7 指定位置修改

我们可以通过指定下标或者查找指定值的下标来修改任意位置的值。

(1) 代码实现
void SeqListModify(SeqList* p, int pos, SLDataType x)
{
	assert(p);  //断言
	assert(p->size > 0);  //顺序表不能为空
	assert(pos >= 0 && pos < p->size);  //检查pos下标的合法性
	p->a[pos] = x;  //修改pos下标处对应的数据
}
(2) 复杂度分析
  • 时间复杂度:因为是通过指定下标修改,所以时间复杂度为O(1)。
  • 空间复杂度:没有开辟额外的空间,所以空间复杂度为O(1)。

4.8 指定位置插入

和前面其他插入一样,指定位置插入也需要判断是否扩容。

(1) 代码实现
void SeqListInsert(SeqList* p, int pos, SLDataType x)
{
	assert(p);//断言
	assert(pos <= p->size); //检查pos下标的合法性
	SeqListCheck(p);//扩容
	int cur = p->size;
	while (cur > pos)
	{
		p->a[cur] = p->a[cur - 1];//覆盖
		--cur;
	}
	p->a[pos] = x;
	p->size++;
}
(2) 复杂度分析
  • 时间复杂度:需要依次覆盖,时间复杂度为O(N)。
  • 空间复杂度:可能需要扩容,空间复杂度为O(N)。

4.9 指定位置删除

和前面的删除差不多,但是指定删除可能会依次覆盖。

(1) 代码实现
void SeqListErase(SeqList* p, int pos)
{
	assert(p);  //断言
	assert(p->size > 0);  //顺序表不能为空
	assert(pos >= 0 && pos < p->size);  //检查pos下标的合法性
	int i = 0;
	for (i = pos + 1; i < p->size; i++)  //将pos位置后面的数据依次向前挪动一位
	{
		p->a[i - 1] = p->a[i];
	}
	p->size--;  //有效数据个数-1
}
(2) 复杂度分析
  • 时间复杂度:需要依次覆盖,时间复杂度为O(N)。
  • 空间复杂度:没有额外的空间消耗,空间复杂度为O(1)。

4.10 回收空间

在结束操作之后,需要释放开辟的空间,以防止内存泄漏。

(1) 代码实现
void SeqListDestory(SeqList* p)
{
	assert(p);  //断言
	free(p->a);   //释放动态开辟的空间
	p->a = NULL;  //置空
	p->size = 0;  //数据个数置0
	p->capacity = 0;  //空间容量大小置0
}
(2) 复杂度分析
  • 时间复杂度:没有额外的时间消耗,时间复杂度为O(1)。
  • 空间复杂度:没有额外的空间消耗,空间复杂度为O(1)。

5. 完整代码

5.1 SeqList.h

#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#define N 4 //初始化空间大小
typedef int SLDataType; //类型重命名,方便后续修改类型
typedef struct SeqList
{
	SLDataType* a;    //指向动态开辟的数组
	size_t size;      //有效数据个数
	size_t capacity;  //容量大小
}SeqList;

void SeqListInit(SeqList* p);//初始化顺序表
void SeqListPrint(const SeqList* p);//打印顺序表
void SeqListPushBack(SeqList* p, SLDataType x);//尾插
void SeqListPopBack(SeqList* p);//尾删
void SeqListPushFront(SeqList* p, SLDataType x);//头插
void SeqListPopFront(SeqList* p);//头删
int SeqListFind(const SeqList* p, SLDataType x);//查找数据
void SeqListModify(SeqList* p, int pos, SLDataType x);//修改数据
void SeqListInsert(SeqList* p, int pos, SLDataType x);//指定插入
void SeqListErase(SeqList* p, int pos);//指定删除
void SeqListDestory(SeqList* p);//释放空间

5.2 SeqList.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"SeqList.h"
void SeqListInit(SeqList* p)
{
	assert(p);  //断言
	p->a =(SLDataType*)malloc(N*sizeof(SLDataType));  //初始顺序表为四个空间
	if (p == NULL)
	{
		perror("malloc fail:");
		return ;
	}
	p->size = 0;  //初始数据个数为0
	p->capacity = 4;  //初始空间容量为4
}
void CheckCapacity(SeqList* p)
{
	assert(p);  //断言

	if (p->size == p->capacity)  //检查容量,满了则增容
	{
		size_t newcapacity = 2 * p->capacity;  //,扩容为原来的2倍
		SLDataType* prt = (SLDataType*)realloc(p->a, newcapacity * sizeof(SLDataType));  //扩容
		if (prt == NULL)
		{
			perror("realloc");
			return ;
		}
		p->a = prt;  // prt不为空,开辟成功
		p->capacity = newcapacity;  //更新容量
	}
}
void SeqListPushBack(SeqList* p, SLDataType x)
{
	assert(p);  //断言
	CheckCapacity(p);  //检查顺序表容量是否已满
	p->a[p->size] = x;  //尾插数据
	p->size++;  //有效数据个数+1
}
void SeqListPrint(const SeqList* p)
{
	assert(p);  //断言
	if (p->size == 0)  //判断顺序表是否为空
	{
		printf("顺序表为空\n");
		return;
	}

	int i = 0;
	for (i = 0; i < p->size; i++)  //打印顺序表
	{
		printf("%d ", p->a[i]);
	}
	printf("\n");
}
void SeqListPopBack(SeqList* p)
{
	assert(p);  //断言
	assert(p->size > 0);  //顺序表不能为空
	p->size--;  //有效数据个数-1
}
void SeqListPushFront(SeqList* p, SLDataType x)
{
	assert(p);  //断言
	CheckCapacity(p);  //检查顺序表容量是否已满

	int i = 0;
	for (i = p->size - 1; i >= 0; i--)  //顺序表中[0,size-1]的元素依次向后挪动一位
	{
		p->a[i + 1] = p->a[i];
	}
	p->a[0] = x;  //头插数据
	p->size++;  //有效数据个数+1
}
void SeqListPopFront(SeqList* p)
{
	assert(p);  //断言
	assert(p->size > 0);  //顺序表不能为空

	int i = 0;
	for (i = 1; i < p->size; i++)  //顺序表中[1,size-1]的元素依次向前挪动一位
	{
		p->a[i - 1] = p->a[i];
	}
	p->size--;  //有效数据个数-1
}
int SeqListFind(const SeqList* p, SLDataType x)
{
	assert(p);  //断言

	int i = 0;
	for (i = 0; i < p->size; i++)
	{
		if (p->a[i] == x)
		{
			return i;  //查找到,返回该值在数组中的下标
		}
	}
	return -1;  //没有查找到
}
void SeqListModify(SeqList* p, int pos, SLDataType x)
{
	assert(p);  //断言
	assert(p->size > 0);  //顺序表不能为空
	assert(pos >= 0 && pos < p->size);  //检查pos下标的合法性
	p->a[pos] = x;  //修改pos下标处对应的数据
}
void SeqListInsert(SeqList* p, int pos, SLDataType x)
{
	assert(p);//断言
	assert(pos <= p->size); //检查pos下标的合法性
	SeqListCheck(p);//扩容
	int cur = p->size;
	while (cur > pos)
	{
		p->a[cur] = p->a[cur - 1];//覆盖
		--cur;
	}
	p->a[pos] = x;
	p->size++;
}
void SeqListErase(SeqList* p, int pos)
{
	assert(p);  //断言
	assert(p->size > 0);  //顺序表不能为空
	assert(pos >= 0 && pos < p->size);  //检查pos下标的合法性
	int i = 0;
	for (i = pos + 1; i < p->size; i++)  //将pos位置后面的数据依次向前挪动一位
	{
		p->a[i - 1] = p->a[i];
	}
	p->size--;  //有效数据个数-1
}
void SeqListDestory(SeqList* p)
{
	assert(p);  //断言
	free(p->a);   //释放动态开辟的空间
	p->a = NULL;  //置空
	p->size = 0;  //数据个数置0
	p->capacity = 0;  //空间容量大小置0
}

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

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

相关文章

购买腾讯云服务器请先领取代金券,2024腾讯云优惠

腾讯云优惠代金券领取入口共三个渠道&#xff0c;腾讯云新用户和老用户均可领取8888元代金券&#xff0c;可用于云服务器等产品购买、续费和升级使用&#xff0c;阿腾云atengyun.com整理腾讯云优惠券&#xff08;代金券&#xff09;领取入口、代金券查询、优惠券兑换码使用方法…

C++真题列表

题目解析&#xff1a;RAM是闪存&#xff0c;只要一关机一拔电&#xff0c;就会丢失数据 题目解答&#xff1a;A 题目解析&#xff1a;TXT格式是文本文档 题目解答&#xff1a;B 题目解析&#xff1a;IP地址中每一个字节的取值范围是[0~255]&#xff0c;是不可能有256的 题目…

完美解决Iframe嵌入帆软报表出现跨域cookie写不进去的问题

随着google chrome对第三方cookie的限制越来越狠,现在发现之前使用iframe嵌入的帆软报表已经不好使了。官方现在解决iframe嵌入帆软报表出现跨域导致cookie写不进去的方案是主推 统一主域名的方案(谷歌浏览器单点登录失败- FineReport帮助文档 - 全面的报表使用教程和学习资料…

java 基础(核心知识搭配代码)

前言 java的学习分为了上部分以及下部分进行学习&#xff0c;上部分就是对于java的基础知识&#xff0c;面向对象上&#xff0c;面向对象下&#xff0c;异常操作&#xff0c;javaApi&#xff1b;下部主要是集合&#xff0c;泛型&#xff0c;反射&#xff0c;IO流&#xff0c;J…

【HTML】HTML基础5(特殊字符)

目录 特殊字符的作用 常用的特殊字符 使用效果 特殊字符的作用 例如 当我在两个文字间打出空格时 <p>“银河护卫队”系列 在漫威电影宇宙中一直是异数般的存在&#xff0c;不仅因为影片主角是一群反英雄&#xff0c;<strong>与超级英雄相比显得格格不入<…

Ubuntu18.04安装RTX2060显卡驱动+CUDA+cuDNN

Ubuntu18.04安装RTX2060显卡驱动CUDAcuDNN 1 安装RTX2060显卡驱动1.1 查看当前显卡是否被识别1.2 安装驱动依赖1.3 安装桌面显示管理器1.4 下载显卡驱动1.5 禁用nouveau1.6 安装驱动1.7 查看驱动安装情况 2 安装CUDA2.1 查看当前显卡支持的CUDA版本2.2 下载CUDA Toolkit2.3 安装…

41、网络编程/TCP.UDP通信模型练习20240301

一、编写基于TCP的客户端实现以下功能&#xff1a; 通过键盘按键控制机械臂&#xff1a;w(红色臂角度增大)s&#xff08;红色臂角度减小&#xff09;d&#xff08;蓝色臂角度增大&#xff09;a&#xff08;蓝色臂角度减小&#xff09;按键控制机械臂 1.基于TCP服务器的机械臂…

【办公类-21-05】20240227单个word按“段落数”拆分多个Word(成果汇编 只有段落文字 1拆5)

作品展示 背景需求 前文对一套带有段落文字和表格的word进行13份拆分 【办公类-21-04】20240227单个word按“段落数”拆分多个Word&#xff08;三级育婴师操作参考题目1拆13份&#xff09;-CSDN博客文章浏览阅读293次&#xff0c;点赞8次&#xff0c;收藏3次。【办公类-21-04…

多余图片人物有什么办法可以去掉?一分钟教你搞定

在我们的快节奏生活中&#xff0c;旅游已经成为了一种宝贵的放松方式&#xff0c;它让我们有机会暂时逃离日常的喧嚣&#xff0c;感受大自然的宁静与美丽。每一次踏足新的土地&#xff0c;我们都会被各种独特的风景所吸引&#xff0c;从雄伟的山川到细腻的街景&#xff0c;每一…

市场占比不足1.5%,折叠屏手机为何香不起来?

2007年&#xff0c;取消了实体按键键盘的iPhone诞生&#xff0c;不到四年的时间&#xff0c;就将诺基亚变成了历史。依靠率先发起的触屏技术&#xff0c;苹果快速抢占了全球智能手机市场&#xff0c;并掀起了智能机迭代的潮流。 但折叠屏手机显然没那么幸运。2018年&#xff0…

AcWing 788. 逆序对的数量 解题思路及代码

先贴个题目&#xff1a; 以及原题链接&#xff1a; 788. 逆序对的数量 - AcWing题库https://www.acwing.com/problem/content/790/ 这题也是板子题&#xff0c;就是对归并排序的衍生&#xff0c;我们先分析下如果用归并排序对排序区间进行二分的话&#xff0c;逆序对可能出现的…

【DAY07 软考中级备考笔记】数据结构:线性结构,数组矩阵和广义表

数据结构&#xff1a;线性结构&#xff0c;数组矩阵和广义表 3月2日 – 天气&#xff1a;晴 1. 线性表的定义和存储方式 > 这一部分只需要掌握下面的两点即可&#xff1a; > > * 采用顺序存储和链式存储的特点 > * 单链表的插入和删除操作 2. 栈和队列 > 这里需…

antvX6 - Vue自定义节点,并实现多种画布操作,拖拽、缩放、连线、双击、检索等等

一、 首先 antv x6 分为两个版本 低版本和高版本 我这里是使用的2.0版本 并且搭配了相关插件 例如&#xff1a;画布的图形变换、地图等 个人推荐 2.0版本&#xff0c;高版本配置多&#xff0c;可使用相关插件多&#xff0c;但是文档描述小&#xff0c;仍在更新&#xff0c; 低…

【书生·浦语大模型实战营】第三节 课后作业

基于 InternLM 和 LangChain 搭建你的知识库 0.课程链接1.课后作业1.1基础作业&#xff1a;1.2 进阶作业&#xff1a; 0.课程链接 课程标题&#xff1a;基于 InternLM 和 LangChain 搭建你的知识库 课程链接&#xff1a;https://github.com/InternLM/tutorial/blob/main/langch…

【体育】体育锻炼之体能耐力、户外运动、中华武术(附大学生体质健康标准)

程序员养生指南之 【体育】体育锻炼之体能耐力、户外运动、中华武术&#xff08;附大学生体质健康标准&#xff09; 文章目录 一、如何增强耐力与体能&#xff1f;1、耐力训练的原理2、如何进行耐力训练3、如何提高长跑成绩 二、户外运动之绳结与下降1、绳结2、下降 三、中华武…

C语言 for 循环语句的基本格式是什么?

一、问题 for 循环语句在C语⾔中是最为常见的循环语句&#xff0c;其功能强⼤&#xff0c;⽽且⽤法灵活&#xff0c;那么它的基本格式是什么呢&#xff1f; 二、解答 for 语句的⼀般形式为&#xff1a; for(表达式1;表达式2;表达3&#xff09;语句; 每条 for 语句包含三个⽤分…

2023国赛样题路由部分【RIP RIPNG ACLRIP ACLRIPNG ISIS NAT64】

RT1串行链路、RT2串行链路、FW1、AC1之间分别运行RIP和RIPng协议&#xff0c;FW1、RT1、RT2的RIP和RIPng发布loopback2地址路由&#xff0c;AC1 RIP发布loopback2地址路由&#xff0c;AC1 RIPng采用route-map匹配prefix-list重发布loopback2地址路由。RT1配置offset值为3的路由…

ES6 | (二)ES6 新特性(下) | 尚硅谷Web前端ES6教程

文章目录 &#x1f4da;迭代器&#x1f407;定义&#x1f407;工作原理&#x1f407;自定义遍历数据 &#x1f4da;生成器函数&#x1f407;声明和调用&#x1f407;生成器函数的参数传递&#x1f407;生成器函数案例 &#x1f4da;Promise&#x1f4da;Set&#x1f407;Set的定…

输入一个整数,输出其最长连续因子。

输入一个整数&#xff0c;输出其最长连续因子。 例如 输入&#xff1a;60 输出&#xff1a;2 3 4 5 6 注意&#xff1a;1不算因子 输入输出格式 输入描述: 输入一个整数N&#xff0c;N<10000。 输出描述: 输出其最长连续因子&#xff0c;如果有多个最长&#xff0c;输出…