【数据结构】顺序表(线性表)的实现

news2024/9/28 23:36:30

目录

         一、什么是顺序表?

         二、顺序表的动态实现

               1、顺序表初始化

               2、顺序表打印

               3、顺序表检查空间

               4、顺序表尾插

               5、顺序表尾删

               6、顺序表头插

               7、顺序表头删

               8、顺序表指定位置插入

               9、顺序表指定位置删除

               10、顺序表查找

               11、顺序表销毁

         三、源代码

               1、SeqList.h

               2、SeqList.c

               3、test.c

 


 

一、什么是顺序表?

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

顺序表的结构如下:

 

顺序表一般分为两种:

1、静态顺序表:使用定长数组存储元素。

#define Max 10 
typedef int SLDataType;
typedef struct SeqList
{
	SLDataType data[Max];  //定长数组
	size_t size;  //有效数据的个数
}SeqList;

2、动态顺序表:使用动态开辟的数组存储。

typedef int SLDataType;
typedef struct SeqList
{
	SLDataType* a;      // 指向动态开辟数组的指针
	size_t size;        // 有效数据个数
	size_t capicity;    // 容量空间的大小
}SeqList;

本文主要使用的是动态顺序表,因为静态顺序表是事先已经设置好数组空间的大小,万一需要的空间大小比事先设置的大,此时就会出现溢出的情况,使用不方便,所以在这里使用动态顺序表,用多少开辟多少的数组空间。

二、顺序表的动态实现

  1、顺序表初始化

//初始化
void SLInit(SL* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

初始化的时候可以给顺序表开辟空间,当然也可以直接将指针a给设置为NULL,size和capacity同样设置为0。

  2、顺序表打印

//打印
void SLPrint(SL* ps)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}

  3、顺序表检查空间

在顺序表插入和删除的时候会涉及到顺序表空间大小的问题,在插入的时候首先要判断一下,空间是否已满,满的情况要继续开辟空间,才能够插入。

void SLCheckCapacity(SL* ps)
{
	assert(ps);
	//扩容
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}

在这里之所以将检查封装为一个检查空间的函数,就是为了后面在插入的时候方便使用。

这个顺序表在刚创建的时候,其容量是0,此时我们就给它开辟4个元素大小的空间,当容量满的时候,我们就直接将容量翻倍。当我们在realloc函数中传入的是一个空指针的时候,此时的realloc函数就相当于一个malloc函数,所以在这里直接使用realloc函数也是可以的。

  4、顺序表尾插

//尾插 O(1)
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}

首先我们要检查一下数组空间的大小,如果容量不满可以直接插入,如果容量已满就需要先扩容,在进行插入。 SLCheckCapacity()这个函数就是要检查容量是否已满,满的情况就会立即扩容。

  

  5、顺序表尾删

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	///暴力检查
	assert(ps->size > 0);
	ps->size--;
}

尾删其实只用size--就行,但是要注意一个问题就是在size = 0 的时候,顺序表本身就为空,那么就不能再减了,防止越界访问,因此在这里面加了两个断言就可以防止这种情况的发生。

  6、顺序表头插

//头插 O(N)
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//检查容量够不够
	SLCheckCapacity(ps);

	//挪动数据
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}

头插首先肯定也要检查容量的大小, 保证容量大小充足,然后再将存入的数据整体向后移动一位,将第一个位置给空出来,最后插入新的元素。

  7、顺序表头删

//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	int begin = 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	//暴力检查
	ps->size--;
}

头删和尾删的大体逻辑是相同的,我们首先需要检查一下size是否为0。接下来的操作我们只需要让后面的数据覆盖前面的数据,最终就完成了头删的效果。

  8、顺序表指定位置插入 

任意位置插入就像是头插和尾插的升级版,只不过多了一个参数pos,如果pos在开始位置,相当于头插,如果pos在最后面,就相当于尾插。

//在pos的位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos <= ps->size);
	//检查容量够不够
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}

假设插入位置的下标是pos,那么我们只需要将pos位置到最后的所有数据整体向后移动,然后把pos的位置空出来,再在pos的位置插入一个新的元素,最后size++。

注意:assert(pos >= 0);assert(pos <= ps->size);这两句代码就是限制插入的元素是在0到size之间,而不是在其他地方,保证元素与元素之间是连续存储的。

 

  9、顺序表指定位置删除 

//在pos的位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos < ps->size);
	//挪动数据覆盖
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

首先需要断言一下size是否为0。然后将pos后面的数据整体向前移动,覆盖原来pos位置所在的数据,最终size–。

  10、顺序表查找

int SLFind(SL* ps, SLDataType x, int begin)
{
	assert(ps);

	for (int i = begin; i < ps->size; ++i)
	{
		if (ps->a[i] == x)
		{
			return i;
		}
	}
	return -1;
}

这个函数接口就很简单了,只需遍历一遍数组就行,找到就返回元素的下标,找不到返回-1。

  11、顺序表销毁

//销毁
void SLDestory(SL* ps)
{
	assert(ps);
	//if (ps->a != NULL)
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}

我们开始的时候动态开辟的空间是在堆区申请的,我们用完之后一定要将它释放,防止内存泄漏。

三、源代码

  1、SeqList.h

#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

//动态的顺序表 - 按需扩空间
#define N  10
typedef int SLDataType;

typedef struct SeqList
{
	SLDataType* a;
	int size;//记录存储多少个有效数据
	int capacity;//空间容量大小
}SL;

void SLPrint(SL* ps);//打印
void SLInit(SL* ps);//初始化
void SLDestory(SL* ps);//销毁
void SLCheckCapacity(SL* ps);//检查空间大小

//尾插
void SLPushBack(SL* ps, SLDataType x);
//尾删
void SLPopBack(SL* ps);

//头插
void SLPushFront(SL* ps, SLDataType x);
//头删
void SLPopFront(SL* ps);

//中间的插入
//在pos位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x);
//中间的删除
//删除pos位置的数据
void SLErase(SL* ps, int pos);

//查找
int SLFind(SL* ps, SLDataType x);

  2、SeqList.c

#include "SeqList.h"

//打印
void SLPrint(SL* ps)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}


//初始化
void SLInit(SL* ps)
{
	assert(ps);
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

//销毁
void SLDestory(SL* ps)
{
	assert(ps);
	//if (ps->a != NULL)
	if (ps->a)
	{
		free(ps->a);
		ps->a = NULL;
		ps->capacity = 0;
		ps->size = 0;
	}
}

void SLCheckCapacity(SL* ps)
{
	assert(ps);
	//扩容
	if (ps->size == ps->capacity)
	{
		int newCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SLDataType* tmp = (SLDataType*)realloc(ps->a, newCapacity * sizeof(SLDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity = newCapacity;
	}
}
//尾插 O(1)
void SLPushBack(SL* ps, SLDataType x)
{
	assert(ps);
	SLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}
//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	//暴力检查
	assert(ps->size > 0);
	ps->size--;
}

//头插 O(N)
void SLPushFront(SL* ps, SLDataType x)
{
	assert(ps);
	//检查容量够不够
	SLCheckCapacity(ps);

	//挪动数据
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}
//头删
void SLPopFront(SL* ps)
{
	assert(ps);
	assert(ps->size > 0);
	//挪动数据
	int begin = 1;
	while (begin < ps->size)
	{
    	ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	//暴力检查
	ps->size--;
}

//在pos的位置插入数据
void SLInsert(SL* ps, int pos, SLDataType x)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos <= ps->size);
	//检查容量够不够
	SLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= pos)
	{
		ps->a[end + 1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}
//在pos的位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps);
	assert(pos >= 0);
	assert(pos < ps->size);
	//挪动数据覆盖
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

//查找
int SLFind(SL* ps, SLDataType x)
{
	assert(ps);
	for (int i = 0; i < ps->size; ++i)
	{
		if (ps->a[i] == x)
		{
            //找到
			return i;
		}
	}
    //没找到
	return -1;
}

  3、test.c

#define _CRT_SECURE_NO_WARNINGS 1	
#include"SeqList.h"
void menu()
{
	printf("*******************************\n");
	printf("******  0.退出   1.打印  ******\n");
	printf("******  2.尾插   3.尾删  ******\n");
	printf("******  4.头插   5.头删  ******\n");
	printf("******  6.任意插 7.任意删******\n");
	printf("******  8.在pos位置插入  ******\n");
	printf("******  9.删除pos元素值  ******\n");
	printf("*******************************\n");
}

int main()
{
	int input = 1;
	SL s;
	SLInit(&s);	//创建一个顺序表
	while (input)
	{
		int pos = 0;
		SLDatatype x = 0;
		SLDatatype y = 0;
		menu();
		printf("请选择:>");
		scanf("%d", &input);
		switch (input)
		{
		case 0:
			printf("退出顺序表!\n");
			SLDestroy(&s);
			break;
		case 1:
			SLPrint(&s);
			break;
		case 2:
			printf("请输入一个值:>");
			scanf("%d", &x);
			SLPushBack(&s, x);
			break;
		case 3:
			SLPopBack(&s);
			break;
		case 4:
			printf("请输入一个值:>");
			scanf("%d", &x);
			SLPushFront(&s, x);
			break;
		case 5:
			SLPopFront(&s);
			break;
		case 6:
			printf("请输入下标和目标值:>");
			scanf("%d %d", &pos, &x);
			SLInsert(&s, pos, x);
			break;
		case 7:
			printf("请输入下标:>");
			scanf("%d", &pos);
			SLErase(&s, pos);
			break;
		case 8:
			printf("请输入要插入元素值和目标值:>");
			scanf("%d %d", &y, &x);
			SLInsert(&s, SeqListFind(&s, y), x);
			break;
		case 9:
			printf("请输入要删除元素值:>");
			scanf("%d", &y);
			SLErase(&s, SeqListFind(&s, y));
			break;
		default:
			printf("选择错误,请重新选择!\n");
			break;
		}
	}
	return 0;
}


 本文要是有不足的地方,欢迎大家在下面评论,我会在第一时间更正。

  老铁们,记着点赞加关注哦!!!

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

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

相关文章

hnu社交网络作业1

前言&#xff1a;上的是林剑新老师的课程&#xff0c;还是比较有意思的&#xff0c;此博客用来记录作业的学习情况&#xff0c;答案为老师提供的 一、对于图 1&#xff0c;请回答以下问题&#xff0c;并给出相应的计算过程&#xff1a; (1) 计算图 G 中每个顶点的 closeness 中…

Java重点源码回顾——HashMap1.7

1. 概述 public class HashMap<K,V>extends AbstractMap<K,V>implements Map<K,V>, Cloneable, SerializableHashMap在我们的日常使用中非常多&#xff0c;所以今天来阅读下它的源码&#xff0c;了解它具体的设计思想&#xff0c;能够帮助我们扩宽视野。 H…

vmware vcp证书怎么考?vmware vcp证书通过率如何

可为您提供行业领先的虚拟化技术培训和认证服务&#xff0c;这些认证不但会考察您的知识掌握情况和经验水平&#xff0c;还将与您的实际工作职责挂钩。VMware认证按照不同解决方案划分&#xff0c;可分为四条路径&#xff1a;数据中心虚拟化、网络虚拟化、云计算管理和自动化、…

微信小程序项目转uniapp踩坑日记

本文目录一、前言二、转换方式三、后语四、其他&#xff1a;node报错1、包默认C盘存放&#xff0c;而不是安装目录E盘2、正确的环境变量添加3、npm install 命令报错4、npm install -g express报错没有权限一、前言 由于想要把之前完成的微信小程序项目转换成uniapp项目&#…

git入门指南

文章目录Git入门指南前言什么是版本控制系统&#xff08;VCS&#xff09;版本控制系统Git1、概述2、目前比较流行的Git和SVN&#xff0c;区别是什么3、Git安装4、Git的工作区、暂存区、本地仓库、远程仓库5、git的分支6、git的标签7、 实际操作下git常用命令准备操作git cloneg…

Kafka Cluster 扩容 添加副本 重分配分区

Kafka Cluster 扩容 针对kafka集群&#xff0c;可以通过向群集添加新节点来扩展群集。新节点将仅服务于新主题或新分区&#xff0c;现有分区将不会自动重新平衡以使用新节点。如果需要对现有的TOPIC进行重新分配分区&#xff0c;需要运维人员手动进行干预。今天学习下如何对已…

CAPL学习之路-测试功能集函数(故障注入函数)

TestDisableMsg 禁止发送消息,除非调用函数TestSetMsgEvent 使用TestEnableMsg重新启用消息。此函数影响分配CANoe交互层或CANopen仿真的仿真节点 这个函数可以在测试用例中控制Simulation Setup界面仿真节点报文的发送与停止 testcase TCExample() {testDisableMsg(LightSt…

Linux模块代码、编译、加载、卸载一条龙

最近要写一个Linux的内核模块&#xff0c;记录一下内核模块的代码编写、编译、加载和卸载的基本流程&#xff0c;以作备忘&#xff0c;也希望能帮到有需要的同学。 模块代码 //代码来自https://yangkuncn.cn/kernel_INIT_WORK.html //init_works.c #include <linux/kernel…

Docker-compose快速部署PostgreSQL

Docker-compose快速部署PostgreSQL&#xff1a; 利用docker-compose编排工具部署&#xff1a; docker-compose.yml 文件 version: "3.1" services:postgresql:image: postgres:12-alpinecontainer_name: postgresqlenvironment:POSTGRES_DB: postgresPOSTGRES_USE…

Python--数据容器总结

一、数据容器的分类 数据容器可以从一下视角进行简单的分类&#xff1a; 是否支持下标索引 支持&#xff1a;列表、元组、字符串 --序列类型不支持&#xff1a;集合、字典 --非序列类型是否支持重复元素 支持&#xff1a;列表、元组、字符串 --序列类型不支持&#xff1a;集…

自动生成单测代码插件Squaretest

今天来介绍一款工具Squaretest&#xff0c;它是一款自动生成单元测试的插件&#xff0c;会用到它也是因为最近公司上了代码质量管控的指标&#xff0c;会考评各个项目的单元测试覆盖率&#xff0c;以及sonar扫描出来的各种问题。 很多老项目老代码&#xff0c;或者着急交付的项…

第十八讲:神州三层交换机DHCP中继服务的配置

当DHCP客户机和DHCP服务器不在同一个网段时&#xff0c;由DHCP中继传递DHCP报文。增加DHCP中继功能的好处是不必为每个网段都设置DHCP服务器&#xff0c;同一个DHCP服务器可以为很多个子网的客户机提供网络配置参数&#xff0c;即节约了成本又方便了管理。这就是DHCP中继的功能…

vue经历从2.0到3.0更新

​编辑vue专栏收录该内容 9 篇文章2 订阅 订阅专栏 vue经历从2.0到3.0更新之后&#xff0c;简⽽⾔之就是变得更轻&#xff0c;更快&#xff0c;使⽤起来更加⽅便&#xff0c;每⼀次的版本迭代都是对上⼀个版本的升级优化&#xff0c;不管 是对于我们开发者还是对于⽤户体验都…

铁威马NAS教程之使用CloudSync应用轻松同步备份网盘数据

铁威马在TOS 5系统中&#xff0c;将各种云盘的同步集合到了一个应用——CloudSync&#xff0c;更方便用户使用。只需要下载安装CloudSync&#xff0c;就可以在TNAS和各种云盘之间&#xff0c;实现快速安全的数据同步、分享文件。 1.TOS应用中心下载CloudSync应用&#xff1b; …

中国霍尔效应电流传感器市场规模达到了192.71百万美元?

霍尔效应&#xff0c;是指有小电流通过的一个半导体薄片置于磁场中&#xff0c;受到磁场作用影响电流发生偏转&#xff0c;在控制电流的垂直方向上的半导体两侧形成了电压差&#xff0c;该电势差就是霍尔电压。霍尔电压的大小&#xff0c;与磁场强度和半导体内通过的控制电流成…

JSP ssh 在线英语学习平台myeclipse开发oracle数据库MVC模式java编程计算机网页设计

一、源码特点 JSP ssh在线英语学习平台是一套完善的web设计系统&#xff08;系统采用ssh框架进行设计开发&#xff09;&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S 模式开发。开发环境为TOMCAT7.0…

【MySQL从入门到精通】【高级篇】(三十)记一次mysql5.7的新特性derived_merge的坑

您好&#xff0c;我是码农飞哥&#xff08;wei158556&#xff09;&#xff0c;感谢您阅读本文&#xff0c;欢迎一键三连哦。 &#x1f4aa;&#x1f3fb; 1. Python基础专栏&#xff0c;基础知识一网打尽&#xff0c;9.9元买不了吃亏&#xff0c;买不了上当。 Python从入门到精…

深度学习 Day23——利用RNN实现天气预测

深度学习 Day23——利用RNN实现天气预测 文章目录深度学习 Day23——利用RNN实现天气预测一、前言二、我的环境三、前期工作1、导入依赖项2、导入数据3、查看数据基本信息4、查看各列数据的数据类型5、转换数据集中有关时间数据转换为时间格式6、删除Date标签列7、查看所有列的…

多版本并发控制(MVCC)

MVCC机制概述 MVCC&#xff08;Multi-Version Concurrency Control&#xff09;&#xff0c;中文是多版本并发控制&#xff0c;是指在使用READ COMMITTED、REPEATABLE READ这两种隔离级别的事务在执行SELECT操作时访问记录的版本链的过程&#xff0c;从而在不加锁的前提下使不…

基于LSTM三分类的文本情感分析,采用LSTM模型,训练一个能够识别文本postive, neutral, negative三种

基于LSTM三分类的文本情感分析&#xff0c;采用LSTM模型&#xff0c;训练一个能够识别文本postive, neutral, negative三种 &#xff0c;含数据集可直接运行 完整代码下载地址&#xff1a;基于LSTM三分类的文本情感分析 基于LSTM三分类的文本情感分析 背景介绍 文本情感分析…