【数据结构】详解顺序表

news2024/11/19 7:43:13

目录

1.线性表和顺序表

        1.线性表

        2.顺序表 

2.接口的实现

1. 接口1---初始化顺序表

2. 接口2,3---头插,尾插

3. 接口4,5---头删,尾删

4. 接口6,7---插入,删除

5. 接口8---查找

6. 接口9---修改

7. 接口10---打印

8. 接口11---销毁

3.完整代码及效果展示 


1.线性表和顺序表

        1.线性表

        线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...

线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的, 线性表在物理上存储时,通常以数组和链式结构的形式存储。如下:

        2.顺序表 

        顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,线性表中的一种,一般情况下采用数组存储,支持元素的随机访问。在数组上完成数据的增删查改

        顺序表一般可以分为:

        1. 静态顺序表:使用定长数组存储。使用结构体变量来维护每一张顺序表,结构体的第一个成员是存储顺序表的数组,第二个成员则是存储顺序表内的元素个数。

为了提高代码可维护性 ,我们使用宏定义定义数组的元素个数N,并且使用typedef关键字将int重命名为SQDateType,后续如果需要改变顺序表元素类型则只需将int改为其他类型即可,无需修改代码其他地方。

        2. 动态顺序表:使用动态开辟的数组存储,同样使用结构体变量来维护每一张顺序表,不同的是,由于数组是动态开辟的,所以需要增加一个成员来表示每个顺序表当前的容量。

 

2.接口的实现

        谈到数据结构,就少不了各种各样接口的实现。但在这之前,需要说明的是:静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N如果定大了,空间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态地分配空间大小,所以下面我们实现动态顺序表的各种接口,如增删改查

1. 接口1---初始化顺序表

        初始化想必我们都不陌生,每当我们使用局部变量的时候都需要对其进行初始化或者赋值,否则这个变量就是一个随机值,会导致程序出错。我们一般在定义变量的同时对其进行初始化,这是一个良好的编程习惯。顺序表也是如此,在使用之前,我们先对其进行初始化:

//对顺序表进行初始化
void SeqListInit(SL* s)   //SL为结构体类型的重命名
{
	s->a = NULL;
	s->size = 0;
	s->capacity = 0;
}

 需要注意的是,我们需要传入一个结构体指针,否则修改的只是形参的内容。

2. 接口2,3---头插,尾插

        在实现插入的接口时,我们要考虑到顺序表的空间是否满了。如果满了,则将数组的容量扩大两倍,我们可以将扩容封装为函数提高代码的复用。如果数组空间足够,则将数据插入,尾插则直接将数据插入尾部,头插则需要先将数据向后移动一位,然后再插入头部。

//检查顺序表是否满了,满了则扩容
void CheckSpace(SL* s)
{
	//空间满了
	if (s->size == s->capacity)
	{
		int newcapcity = (s->capacity == 0 ? 4 : 2 * s->capacity);  //计算新空间大小
		SQDateType* tmp = (SQDateType*)realloc(s->a, newcapcity*sizeof(SQDateType));
		if (tmp == NULL)
		{
			exit(-1); //扩容失败,退出程序
		}
		else
		{
			s->a = tmp;
			s->capacity = newcapcity;  //更新空间
		}
	}
}

//尾插
void SeqListPushBack(SL* s, SQDateType x)
{
	CheckSpace(s);
	(s->a)[s->size] = x; //从尾部插入
	(s->size)++; //顺序表元素加一
}

//头插
void SeqListPushFront(SL* s, SQDateType x)
{
	CheckSpace(s);
	for (int i = (s->size) - 1; i >= 0; i--) //集体后移一位
	{
		(s->a)[i + 1] = (s->a)[i];
	}
	(s->a)[0] = x; //从头部插入
	(s->size)++; //顺序表元素加一
}

3. 接口4,5---头删,尾删

        由于我们使用size来维护顺序表的元素个数,所以我们只需将size--即可实现尾删;而头删则是从第二个元素开始向前覆盖一个元素,然后size--,即可完成头删。在执行删除操作时,要保证顺序表不为空

//尾删
void SeqListPopBack(SL* s)
{
	assert(s->size > 0); //判断顺序表是否为空
	(s->size)--;
}

//头删
void SeqListPopFront(SL* s)
{
	assert(s->size > 0);
	for (int i = 1; i < s->size; i++) //第二个元素起向前覆盖
	{
		(s->a)[i - 1] = (s->a)[i];
	}
	(s->size)--;
}

4. 接口6,7---插入,删除

        我们可以将头删尾删,头插尾插4个接口进行扩展,使其能在任意合法位置进行插入或删除,如下:

void SeqListErase(SL* s, int pos)
{
	assert(pos < (s->size));  //检查位置是否合法
	int start = pos + 1;
	for (int i = start; i < s->size; i++)
	{
		(s->a)[i - 1] = (s->a)[i];
	}
	(s->size)--;
}

void SeqListInsert(SL* s, int pos, SQDateType x)
{
	assert(pos <= s->size);
	CheckSpace(s);
	int end = s->size-1;
	for (int i = end; i >= pos; i--)
	{
		(s->a)[i + 1] = (s->a)[i];
	}
	(s->a)[pos] = x;
	(s->size)++;
}

5. 接口8---查找

        遍历顺序表,当找到需要查找的元素时则返回对应的下标,如果不存在则返回-1,因为-1不会是任何数的下标。

//查找
int SeqListFind(SL* s, SQDateType x)
{
	for (int i = 0; i < s->size; i++)  //遍历顺序表
	{
		if ((s->a)[i] == x)
		{
			return i;
		}
	}
    //没找到
	return -1;
}

6. 接口9---修改

        作用是将用户指定位置的元素修改为指定元素,通过下标直接进行修改即可。同样的,需要先检查指定的位置是否合法:

//修改
void SeqListModify(SL* s, int pos, SQDateType x)
{
	assert(pos < s->size);  //位置是否合法
	(s->a)[pos] = x;
}

7. 接口10---打印

        有了上述接口,我们就算完成了一个顺序表增删查改基本功能的实现了。我们可以调用上面的接口对顺序表进行操作。当我们需要查看我们的操作结果时,我们可以把顺序表的元素打印出来:

//打印
void SeqListPrint(const SL* s)
{
	for (int i = 0; i < (s->size); i++)
	{
		printf("%d ", (s->a)[i]);
	}
}

8. 接口11---销毁

        由于我们使用的是动态顺序表,空间是通过realloc向系统申请,开辟在堆上的。当我们不需要时,需要手动释放空间,于是我们可以再设计一个接口用于销毁空间:

//销毁
void SeqListDestroy(SL* s)
{
	free(s->a);
	s->a = NULL;
	s->capacity = 0; //容量置0
	s->size = 0; //元素个数置0
}

3.完整代码及效果展示 

        我们可以采用多文件编写的形式,将上述接口的定义实现放在SeqList.c文件中,然后将接口的声明和结构体的定义放于SeqList.h头文件中,以达到封装的效果。这样我们如果需要使用顺序表,就只需要在文件中包含对应的头文件SeqList.h就可以使用我们上面定义的各种接口。以下为本文实现的顺序表完整代码以及效果展示:

//Seqlist.h文件,用于声明接口函数,定义结构体
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
//增强程序可维护性
typedef int SQDateType;
typedef struct SeqList
{
	SQDateType* a;
	int size;   //元素个数
	int capacity;  //容量
}SL;

void SeqListInit(SL* s);
void SeqListPrint(const SL* s);
void SeqListPushBack(SL* s, SQDateType x);
void SeqListPopBack(SL* s);
void SeqListPushFront(SL* s, SQDateType x);
void SeqListPopFront(SL* s);
void SeqListErase(SL* s, int pos);
void SeqListInsert(SL* s,int pos, SQDateType x);
int SeqListFind(SL* s, SQDateType x);
void SeqListModify(SL* s, int pos, SQDateType x);
void SeqListDestroy(SL* s);
//SeqList.c文件,用于定义接口函数
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
void SeqListInit(SL* s)
{
	s->a = NULL;
	s->size = 0;
	s->capacity = 0;
}

void SeqListPrint(const SL* s)
{
	for (int i = 0; i < (s->size); i++)
	{
		printf("%d ", (s->a)[i]);
	}
}
void CheckSpace(SL* s)
{
	//空间满了
	if (s->size == s->capacity)
	{
		int newcapcity = (s->capacity == 0 ? 4 : 2 * s->capacity);
		SQDateType* tmp = (SQDateType*)realloc(s->a, newcapcity*sizeof(SQDateType));
		if (tmp == NULL)
		{
			exit(-1);
		}
		else
		{
			s->a = tmp;
			s->capacity = newcapcity;
		}
	}
}
void SeqListPushBack(SL* s, SQDateType x)
{
	CheckSpace(s);
	(s->a)[s->size] = x;
	(s->size)++;
}

void SeqListPushFront(SL* s, SQDateType x)
{
	CheckSpace(s);
	for (int i = (s->size) - 1; i >= 0; i--)
	{
		(s->a)[i + 1] = (s->a)[i];
	}
	(s->a)[0] = x;
	(s->size)++;
}


void SeqListPopBack(SL* s)
{
	assert(s->size > 0);
	(s->a)[s->size - 1] = 0;
	(s->size)--;

}

void SeqListPopFront(SL* s)
{
	assert(s->size > 0);
	for (int i = 1; i < s->size; i++)
	{
		(s->a)[i - 1] = (s->a)[i];
	}
	(s->size)--;
}

void SeqListErase(SL* s, int pos)
{
	assert(pos < (s->size));
	int start = pos + 1;
	for (int i = start; i < s->size; i++)
	{
		(s->a)[i - 1] = (s->a)[i];
	}
	(s->size)--;
}

void SeqListInsert(SL* s, int pos, SQDateType x)
{
	assert(pos <= s->size);
	CheckSpace(s);
	int end = s->size-1;
	for (int i = end; i >= pos; i--)
	{
		(s->a)[i + 1] = (s->a)[i];
	}
	(s->a)[pos] = x;
	(s->size)++;
}


int SeqListFind(SL* s, SQDateType x)
{
	for (int i = 0; i < s->size; i++)
	{
		if ((s->a)[i] == x)
		{
			return i;
		}
	}
	return -1;
}

void SeqListModify(SL* s, int pos, SQDateType x)
{
	assert(pos < s->size);
	(s->a)[pos] = x;
}

void SeqListDestroy(SL* s)
{
	free(s->a);
	s->a = NULL;
	s->capacity = 0;
	s->size = 0;
}

       最后, 我们在text.c文件调用顺序表各个接口进行测试,如下:

//text.c文件,用于测试
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"

void TextSeqList()
{
	SL s;
	SeqListInit(&s);
	//尾插
	printf("尾插前\n");
	SeqListPrint(&s);
	SeqListPushBack(&s, 1);
	SeqListPushBack(&s, 2);
	SeqListPushBack(&s, 3);
	printf("\n尾插后\n");
	SeqListPrint(&s);
	//头插
	printf("\n头插前\n");
	SeqListPrint(&s);
	SeqListPushFront(&s, 1);
	SeqListPushFront(&s, 2);
	SeqListPushFront(&s, 3);
	printf("\n头插后\n");
	SeqListPrint(&s);
	//头删
	printf("\n头删前\n");
	SeqListPrint(&s);
	SeqListPopFront(&s);
	printf("\n头删后\n");
	SeqListPrint(&s);
	//尾删
	printf("\n尾删前\n");
	SeqListPrint(&s);
	SeqListPopBack(&s);
	printf("\n尾删后\n");
	SeqListPrint(&s);
	//删除
	printf("\n删除二号下标前\n");
	SeqListPrint(&s);
	SeqListErase(&s, 2);
	printf("\n删除二号下标后\n");
	SeqListPrint(&s);
	//插入
	printf("\n在2号下标插入200前\n");
	SeqListPrint(&s);
	SeqListInsert(&s, 2, 200);
	printf("\n在2号下标插入200后\n");
	SeqListPrint(&s);
	//查找
	printf("\n元素200所在下标为:%d\n",SeqListFind(&s, 200));
	//修改
	printf("\n修改在2号下标为300前\n");
	SeqListPrint(&s);
	SeqListModify(&s, 3, 300);
	printf("\n修改在2号下标为300后\n");
	SeqListPrint(&s);
	//销毁
	SeqListDestroy(&s);
}
int main()
{
	TextSeqList();
	return 0;
}

        以下就是测试的最终效果:


 以上,就是本期的全部内容。

制作不易,能否点个赞再走呢qwq

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

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

相关文章

什么是HTTPS?为什么要为您的网站购买一个?

HTTPS或安全超文本传输协议是HTTP的安全版本&#xff0c;是一种用于在Web浏览器和网站之间通过Web传输数据的协议。HTTPS通过使用一种称为传输层安全性(TLS)的加密协议对服务器和浏览器之间传递的所有数据进行加密&#xff0c;以提高安全性&#xff0c;该协议前面是SSL&#xf…

【Pytorch项目实战】之人脸检测与识别:基于face_recognition开源人脸识别库

文章目录人脸检测与识别&#xff08;一&#xff09;实战&#xff1a;人脸检测&#xff08;图片&#xff09;&#xff08;二&#xff09;实战&#xff1a;人脸检测与识别&#xff08;视频&#xff09;人脸检测与识别 face_recognition 是开源人脸识别库。Face Recognition官网。…

启科 QuTrunk+Runtime+QuSaaS+亚马逊云科技量子计算编程实战

QuTrunk 是启科量子自主研发的一款免费、开源、跨平台的量子计算编程框架&#xff0c;包括量子编程 API、量子命令转译、量子计算后端接口等。它提供多种量子计算体验&#xff0c;提供本地量子计算 Python 计算后端&#xff0c;提供 OMP 多线程、MPI 多节点并行、GPU 加速等计算…

操作系统权限提升(一)之操作系统权限介绍

前言 操作系统权限提升简称提权,顾名思义就是提升自己在目标系统中的权限。现在的操作系统都是多用户操作系统,用户之间都有权限控制,比如通过Web漏洞拿到的是web进程的权限,往往Web服务都是以一个权限很低的账号启动的,因此通过 Webshell进行一些操作会受到限制,这就需要将其…

Leetcode力扣秋招刷题路-0094

从0开始的秋招刷题路&#xff0c;记录下所刷每道题的题解&#xff0c;帮助自己回顾总结 94. 二叉树的中序遍历&#xff08;Easy&#xff09; 给定一个二叉树的根节点 root &#xff0c;返回它的 **中序 **遍历。 示例 1&#xff1a; 输入&#xff1a;root [1,null,2,3] 输出…

无线网络概论_4

成帧细节 数据帧&#xff1a;好比802.11中的驮马&#xff0c;负责在工作站直接板运输数据。控制帧&#xff1a;通常能够与数据帧搭配使用&#xff0c;负责区域的清空&#xff0c;信道的取得以及载波监听的维护并于收到数据时给予肯定确认&#xff0c;借此来增加数据传输的可靠…

垃圾收集器必问系列—CMS

本文已收录至Github&#xff0c;推荐阅读 &#x1f449; Java随想录 应该相信&#xff0c;自己是生活的战胜者。——雨果 文章目录CMS简介运作过程CMS的缺陷处理器资源敏感无法处理“浮动垃圾”内存碎片纵观全书《深入理解JVM虚拟机》第三版&#xff0c;在垃圾回收器这一篇章&a…

CMakelists.txt简单介绍

先祝大家开工大吉&#xff0c;这篇策划一下CMakelists.txt&#xff0c;它依托在Cmake里&#xff0c;在大型工程、平台兼容方面非常好用的C项目编译工具&#xff0c;再此先对它做一个简短的介绍。 目录 一、CMakelists.txt简介 二、使用示例 三、总结 一、CMakelists.txt简介…

Eclipse swt环境搭建

通过Eclipse Marketplace安装打开eclipse&#xff0c;点击help&#xff0c;在弹出的框中&#xff0c;选择Eclipse Marketplace。如下所示。。。搜索windows 安装插件一、创建一个SWT项目点击左上角的File - > New -> Other...进入后搜索SWT ,选中在WindowBuilder下的SWT …

CnOpenData中国彩票销售数据

一、数据简介 彩票是一种编有号码的证券&#xff0c;按票面价格出售。开奖后&#xff0c;持有中奖号码奖券的&#xff0c;可按规定领奖。它是一种建立在机会均等基础上的、具有公平竞争特征的娱乐游戏。国家发行彩票的目的是筹集社会公众资金&#xff0c;资助福利、体育等社会公…

springboot中整合mybatis及简单使用

springboot中整合mybatis及简单使用1.引入依赖2.在applicaiton.yaml中配置数据源以及mybatis3.创建sql测试表4.编写mapper接口和mapper.xml文件4.1 mapper接口4.2 mapper.xml4.3 mybatisX快速开发插件5.创建service层和controller层5.1 创建service层5.2 创建controller层6.项目…

[网鼎杯 2018]Fakebook

目录 信息收集 代码审计 思路 解法一&#xff08;非预期解&#xff09; 解法二&#xff08;预期解&#xff09; 信息收集 目录扫描代码泄露扫描 robots.txt;flag.php;login.php;user.php;view.php;join.php; 直接打开flag.php后没有回显&#xff0c;应该需要通过ssrf来访…

Qt音视频开发12-easyplayer内核

一、前言 在视频监控行业经常看到两个厂家广告打得比较厉害&#xff0c;一个是青犀视频对应easyplayer&#xff0c;一个是大牛直播&#xff0c;两个最初都是sdk免费&#xff0c;并提供调用示例源码&#xff0c;后面大牛直播的sdk以及示例都无法运行&#xff0c;目前就剩下免费…

Word处理控件Aspose.Words功能演示:如何在 C# .NET 中将 PNG、JPG 转换为 Word

Aspose.Words 是一种高级Word文档处理API&#xff0c;用于执行各种文档管理和操作任务。API支持生成&#xff0c;修改&#xff0c;转换&#xff0c;呈现和打印文档&#xff0c;而无需在跨平台应用程序中直接使用Microsoft Word。此外&#xff0c; Aspose API支持流行文件格式处…

git push clone 参数具体解析

1 问题背景 git远程仓库有很多命令&#xff0c;但是教程里面讲解的都是特别模糊的。 2 命令实例解析 2.1 git branch -vv -a命令具体解析 在开始下面的命令解析之前&#xff0c;我们一定要学会git branch -vv -a这个命令&#xff0c;这个是查询本地仓库远程仓库跟踪关系最全…

《从0开始学大数据》之ZooKeeper是如何保证数据一致性的

背景 在分布式集群系统中&#xff0c;比如两个应用程序都需要对一个文件路径进行写操作&#xff0c;但是如果两个应用程序对于哪台服务器是主服务器的判断不同&#xff0c;就会分别连接到两个不同的 NameNode 上&#xff0c;并都得到了对同一个文件路径的写操作权限&#xff0…

振弦采集模块配置工具VMTool 扩展功能指令生成器与实时曲线

振弦采集模块配置工具VMTool 扩展功能指令生成器与实时曲线 指令生成器 &#xff08; 1&#xff09; 指令生成 指令生成器可根据需要生成符合 MODBUS 和 AABB 通讯协议的读取和控制指令。 通过点击串口调试工具内的【 指令生成器】 按钮&#xff0c;可打开指令生成器窗口&#…

【SpringBoot高级篇】SpringBoot集成XXL-JOB分布式任务调度平台

【SpringBoot高级篇】SpringBoot集成XXL-JOB分布式任务调度平台简介下载源码部署任务调度平台执行SQL脚本部署任务平台简单使用初始化测试项目pom依赖SampleXxlJob修改配置调度平台配置执行器任务管理添加任务简介 XXL-JOB是一个分布式任务调度平台&#xff0c;其核心设计目标…

连续函数的运算与初等函数的连续性——“高等数学”

各位CSDN的uu们你们好呀&#xff0c;今天&#xff0c;小雅兰的内容是连续函数的运算与初等函数的连续性&#xff0c;上篇博客我们学到了函数的连续性和间断点&#xff0c;这篇博客相当于是上篇博客的一个补充&#xff0c;好吧&#xff0c;现在就让我们进入高等数学的世界吧 一、…

【刷题】不用加减乘除做加法

这是一道简单的数学题。但是比较繁琐&#xff0c;需要有耐心。 目录 前言 一、找规律 二、怎么实现加法&#xff1f; 总结 前言 这道题不让用四则运算符做加法&#xff0c;于是我第一反应是用位运算。 难道转成二进制再使用位运算吗&#xff0c;显然不需要&#xff0c;列草稿可…