详解初阶数据结构之顺序表(SeqList)——单文件实现SeqList的增删查改

news2025/2/21 21:51:25

目录

一、线性表

二、顺序表

2.1概念及结构

2.2接口实现

2.3动态顺序表的创建

2.3动态顺序表的初始化

2.3.1传值初始化

2.3.2传址初始化

2.4动态顺序表的清空

2.5动态顺序表的扩容

2.6动态顺序表内容的打印

三、动态顺序表的使用

3.1尾插尾删

3.1.1尾插

3.1.2尾删

3.2头插头删

3.2.1头插

3.2.2头删

3.3在pos位置插入x

3.4删除pos位置的值

3.5修改某个位置的值

四、完整代码


一、线性表

线性表(linear list)是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使
用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串...
线性表在逻辑上是线性结构,也就说是连续的一条直线。但是在物理结构上并不一定是连续的,
线性表在物理上存储时,通常以数组和链式结构的形式存储。

二、顺序表

2.1概念及结构

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

顺序表一般分为:

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

//静态顺序表
#define N 100
struct SeqList
{
	int a[N];//定长数组
	int size;//有效数据的个数
};

 

缺点:不是很灵活

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

2.2接口实现

静态顺序表只适用于确定知道需要存多少数据的场景。静态顺序表的定长数组导致N定大了,空
间开多了浪费,开少了不够用。所以现实中基本都是使用动态顺序表,根据需要动态的分配空间
大小,所以下面我们实现动态顺序表。

所谓动态其实指的这个结构体里的指针是动态内存开辟来的,是可变的,用的时候动态开辟,不够的话继续开辟,程序结束的时候释放。

2.3动态顺序表的创建

typedef int SLDatatype;//将int重命名为SLDatatype
typedef struct SeqList
{
	SLDatatype* a;//指向动态开辟的数组

	SLDatatype capacity;//容量
	SLDatatype size;//有效数据的个数

}SL;//将结构体SeqList重命名为SL

2.3动态顺序表的初始化

2.3.1传值初始化

//传值初始化
void SLInit(SL s)
{
	s.a = NULL;
	s.size = 0;
	s.capacity = 0;
}

 函数那个章节我们学过形参只是实参的临时拷贝,并没有实际作用,生命周期短,出了函数的作用域就会销毁,我们不考虑这种初始化方式。

2.3.2传址初始化

//传址初始化
void SLInit(SL* ps)
{
	ps->a = 0;
	ps->capacity = 0;
	ps->size = 0;
}
void SLInit(SL* ps)
{
	ps->a = (SLDatatype*)malloc(sizeof(SLDatatype) * 4);//开辟了4个字节的空间
	if (ps->a == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	ps->capacity = 4;//开辟了空间就要给容量赋值
	ps->size = 0;
}

上面两种初始化方式都可以给予结构体成员变量赋值,但是我们使用第二种,因为第二种为我们开辟了空间。


2.4动态顺序表的清空

void SLDestr(SL* ps)
{
	free(ps->a);
	ps->a = NULL;

	ps->capacity = 0;//内存释放,容量清零
	ps->size = 0;//内存释放,有效数据清零
}

2.5动态顺序表的扩容

void SLCheckcapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		SLDatatype* tmp = (SLDatatype*)realloc(ps->a, ps->capacity * 2 *( sizeof(SLDatatype)));//扩容尾原来的倍数

		if (tmp == NULL)
        //判断是否扩容失败
		{
			perror("realloc failed");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;//扩容后修改原来的容量
	}
}

这就是所谓的动态,当我们空间不够时,就需要开辟新的空间,使用realloc函数要注意是否开辟成功,定义一个中间指针,当开辟成功时将这个指针赋值给动态数组中的指针。 

2.6动态顺序表内容的打印

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

size为有效数据个数,使用循环打印其中的有效数据。 

三、动态顺序表的使用

3.1尾插尾删

3.1.1尾插

void SLPushBack(SL* ps, SLDatatype x)
{
	SLCheckcapacity(ps);//检查空间是否足够插入
	ps->a[ps->size] = x;//赋值
	ps->size++;//插入一个有效数据,有效数据个数加一
}

 首先一定要检查规矩是否足够,根据上面开辟的空间,容量为4,有效数据为size为0,所以从第一个空间开始插入数据。

3.1.2尾删

//尾删
void SLPopBack(SL* ps)
{
	assert(ps->size > 0);//判断是否会造成越界
	if (ps->size == 0)
	{
		return;
	}
	ps->size--;//删除一个数据,有效数据个数减一
}

插入数据后size的大小也会变化,数组中最后一个数字的下标刚好和size的大小一样我们只需要将size减1就行。 

3.2头插头删

3.2.1头插

void SLPushFront(SL* ps, SLDatatype x)
{
	SLCheckcapacity(ps);//检查空间是否足够
	int end = ps->size;
	while (end > 0)
	{
		ps->a[end] = ps->a[end - 1];//将前一个数据后移动
		end--;
	}
	ps->a[0] = x;//将x赋给初始位置
	ps->size++;//加入一个数字,有效数据个数加1
}

 老规矩一定要检查空间是否足够,头部插入数据我们只需要将原来的数据往后移动一格就将第一格的位置空出来,再将数值插入就行,插入一个数据,有效数据加1即可。

3.2.2头删

void SLPopFront(SL* ps)
{
	assert(ps->size > 0);//防止越界访问
	if (ps->size==0)
	{
		return;
	}
	int begin = 0;
	while (begin < ps->size)
	{
		ps->a[begin] = ps->a[begin+1];//将后一个数据往前移动
		begin++;
	}
	ps->size--;//减少一个数字,有效数据减1
}

这里的删除并不是真正意义上的删除,我们只需要将原来的数据往前移动一位使后一位的数据覆盖在前一位,这就做到了删除,顺便再将有效数据减1就行。 

3.3在pos位置插入x

void SLInsert(SL* ps, int pos, int x)
{
	assert(pos >= 0 && pos <= ps->size);//防止越界访问
	SLCheckcapacity(ps);
	int end = ps->size;
	while (end >=pos)
	{
		ps->a[end] = ps->a[end-1];//和头插的思想差不多,将数据后移
		end--;
	}
	ps->a[pos] = x;//将x赋值给pos位置
	ps->size++;//有效数据加1
}

我们可以这样理解:将pos看成初始位置,是不是就转化为头插了?按照头插的思想就可以完成在pos位置上插入x。 

3.4删除pos位置的值

void SLErase(SL* ps, int pos)
{
	assert(pos >= 0 && pos <= ps->size);//防止越界访问
	SLCheckcapacity(ps);
	int begin = pos;
	while (begin < ps->size)
	{
		ps->a[begin] = ps->a[begin + 1];//和头删的思想差不多,将数据前移
		begin++;
	}
	ps->size--;//有效数据减1
}

我们会发现3.4和3.5不仅可以做到某个位置值的插入和删除,也可以做到尾插尾删和头插头删。 

3.5修改某个位置的值

void SLModify(SL* ps, SLDatatype pos, SLDatatype x)
{
	assert(pos >= 0 && pos < ps->size);//防止越界
	ps->a[pos] = x;
}

 这样修改某个位置的值看起来是挺麻烦,但是是为了安全考虑。

四、完整代码

#define _CRT_SECURE_NO_WARNINGS 67
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
//静态顺序表
//#define N 100
//struct SeqList
//{
//	int a[N];//定长数组
//	int size;//有效数据的个数
//};


//动态顺序表

//创建
typedef int SLDatatype;
typedef struct SeqList
{
	SLDatatype* a;//指向动态开辟的数组

	SLDatatype capacity;//容量
	SLDatatype size;//有效数据的个数

}SL;
//传值初始化
//void SLInit(SL s)
//{
//	s.a = NULL;
//	s.size = 0;
//	s.capacity = 0;
//}
//传址初始化
//void SLInit(SL* ps)
//{
//	ps->a = 0;
//	ps->capacity = 0;
//	ps->size = 0;
//}
void SLInit(SL* ps)
{
	ps->a = (SLDatatype*)malloc(sizeof(SLDatatype) * 4);//开辟了4个字节的空间
	if (ps->a == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}
	ps->capacity = 4;
	ps->size = 0;
}
//清空
void SLDestr(SL* ps)
{
	free(ps->a);
	ps->a = NULL;

	ps->capacity = 0;
	ps->size = 0;
}
//打印
void SLprint(SL* ps)
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		printf("%d ", ps->a[i]);
	}
	printf("\n");
}
//检查容量
void SLCheckcapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		SLDatatype* tmp = (SLDatatype*)realloc(ps->a, ps->capacity * 2 *( sizeof(SLDatatype)));//扩容尾原来的倍数

		if (tmp == NULL)
		{
			perror("realloc failed");
			exit(-1);
		}
		ps->a = tmp;
		ps->capacity *= 2;
	}
}
//尾插
void SLPushBack(SL* ps, SLDatatype x)
{
	SLCheckcapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}
//尾删
void SLPopBack(SL* ps)
{
	assert(ps->size > 0);
	if (ps->size == 0)
	{
		return;
	}
	ps->size--;
}
//头插
void SLPushFront(SL* ps, SLDatatype x)
{
	SLCheckcapacity(ps);
	int end = ps->size;
	while (end > 0)
	{
		ps->a[end] = ps->a[end - 1];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}
//头删
void SLPopFront(SL* ps)
{
	assert(ps->size > 0);
	if (ps->size==0)
	{
		return;
	}
	int begin = 0;
	while (begin < ps->size)
	{
		ps->a[begin] = ps->a[begin+1];
		begin++;
	}
	ps->size--;
}
//在pos位置插入x
void SLInsert(SL* ps, int pos, int x)
{
	assert(pos >= 0 && pos <= ps->size);
	SLCheckcapacity(ps);
	int end = ps->size;
	while (end >=pos)
	{
		ps->a[end] = ps->a[end-1];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}
//删除pos位置的值
void SLErase(SL* ps, int pos)
{
	assert(pos >= 0 && pos <= ps->size);
	SLCheckcapacity(ps);
	int begin = pos;
	while (begin < ps->size)
	{
		ps->a[begin] = ps->a[begin + 1];
		begin++;
	}
	ps->size--;
}
int SLFind(SL* ps, int x)
{
	int i = 0;
	for (i = 0; i < ps->size; i++)
	{
		if (ps->a[i] == x)
			return i;
	}
	return -1;
}

void SLModify(SL* ps, SLDatatype pos, SLDatatype x)
{
	assert(pos >= 0 && pos < ps->size);
	ps->a[pos] = x;
}
int main()
{
	SL s1;
	//传值初始化
	//SLInit(s1);
	//传址初始化
	SLInit(&s1);
	//尾插
	SLPushBack(&s1, 1);
	SLPushBack(&s1, 2);
	SLPushBack(&s1, 3);
	SLPushBack(&s1, 4);
	SLPushBack(&s1, 5);
	SLPushBack(&s1, 6);
	SLPushBack(&s1, 7);
	//尾插测试
	printf("尾插:\n");
	SLprint(&s1);
	//尾删
	SLPopBack(&s1);
	//尾删测试
	printf("尾删:\n");
	SLprint(&s1);
	//头插
	SLPushFront(&s1,10);
	//头插测试
	printf("头插:\n");
	SLprint(&s1);
	//头删 
	SLPopFront(&s1);
	//头删测试
	printf("头删:\n");
	SLprint(&s1);
	//在pos位置插入x
	SLInsert(&s1, 0, 100);
	//pos插入x测试
	printf("pos位置插入x\n");
	SLprint(&s1);
	//删除pos位置的值
	SLErase(&s1, 0);
	//测试
	printf("删除pos位置的值\n");
	SLprint(&s1);


	//改
	SLModify(&s1, 2, 1);
	printf("修改某个位置上的值:\n");
	//
	SLprint(&s1);
	//清空
	SLDestr(&s1);
	return 0;
}

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

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

相关文章

集合框架和泛型二

一、Set接口 1. Set接口概述 java.util.Set 不包含重复元素的集合、不能保证存储的顺序、只允许有一个 null。 public interface Set<E> extends Collection<E>抽象方法&#xff0c;都是继承自 java.util.Collection 接口。 Set 集合的实现类有很多&#xff0c;…

分布式AKF拆分原则

目录 1 前言2 什么是AKF3 如何基于 AKF X 轴扩展系统&#xff1f;4 如何基于 AKF Y 轴扩展系统&#xff1f;5 如何基于 AKF Z 轴扩展系统&#xff1f;6 小结 1 前言 当我们需要分布式系统提供更强的性能时&#xff0c;该怎样扩展系统呢&#xff1f;什么时候该加机器&#xff1…

网络安全(黑客)技术自学

前言 一、什么是网络安全 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 无论网络、Web、移动、桌面、云等哪个领域&#xff0c;都有攻与防…

高忆管理:突破22万亿!五大保险巨头总资产创历史新高

阅历了几年深度转型的稳妥业正在冲破迷雾。 券商我国记者统计显现&#xff0c;本年上半年&#xff0c;我国人寿、我国人保、我国安全、我国太保、新华稳妥等五大A股上市险企总财物破22万亿元&#xff0c;半年度营收1.5万亿元。从3至5年中长周期来看&#xff0c;稳妥集团公司体…

【大数据】Kafka 入门指南

Kafka 入门指南 1.Kafka 简介2.Kafka 架构3.分区与副本4.偏移量5.消费者组6.总结 1.Kafka 简介 Apache Kafka 是一种高吞吐、分布式的流处理平台&#xff0c;由 LinkedIn 开发并于 2011 年开源。它具有 高伸缩性、高可靠性 和 低延迟 等特点&#xff0c;因此在大型数据处理场景…

“JSR303和拦截器在Java Web开发中的应用与实践“

目录 引言JSR303什么是JSR303?为什么要使用JSR303?常用注解快速入门JSR303 拦截器什么是拦截器拦截器与过滤器应用场景快速入门拦截器 总结 引言 在Java Web开发过程中&#xff0c;我们经常会遇到需要对输入数据进行验证和处理&#xff0c;同时需要对请求进行拦截与控制的需…

纷享销客受邀出席CDIE2023数字化创新博览会 助力大中型企业增长

2023年&#xff0c;穿越周期&#xff0c;用数字化的力量重塑企业经营与增长的逻辑&#xff0c;再次成为企业数字化技术应用思考的主旋律&#xff0c;以数字经济为主线&#xff0c;数字技术融入产业发展与企业增长为依据&#xff0c;推动中国企业数字化升级。 9月5日&#xff0c…

Git多人开发解决冲突案例

准备工作&#xff1a; 1.创建一个gitee远程仓库https://gitee.com/xxxxxxx.git 2.初始化两个本地git仓库用户&#xff0c;目的是模拟多人协作开发时提交代码发生冲突的场景 3.解决冲突并提交。 进入正题&#xff1a; lisi 通过vim指令修改readme.md文件内容&#xff0c;推送到…

合宙Air724UG LuatOS-Air LVGL API控件-表格(Table)

表格&#xff08;Table&#xff09; 示例代码 --创建表格Table1 lvgl.table_create(lvgl.scr_act(),nil)--设置表格为4行5列lvgl.table_set_row_cnt(Table1,4)lvgl.table_set_col_cnt(Table1,5)--给每个单元格赋值lvgl.table_set_cell_value(Table1, 0, 0, "选手")l…

QT生成ICO文件

生成ICO文件 #include <QApplication> #include <QImage> #include <QIcon> #include <QFile> #include <QDebug> #include <QPixmap>int main(int argc, char* argv[]) {QApplication app(argc, argv);// 读取图片文件QImage image(&quo…

Python类的概念

类 类的技术名词解释 ● 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。 ● 类变量&#xff1a;类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用…

功率放大器的定义详解

功率放大器是一种电子放大器&#xff0c;主要用于将输入信号的功率放大到足以驱动负载或输出器件所需的水平。通常&#xff0c;功率放大器会将低电平高电流的输入信号转换成高电平低电流的输出信号&#xff0c;以便给负载提供足够的功率。 功率放大器广泛应用于各种应用场合&am…

(Note)中文EI检索期刊目录

ei和sci、ssci一样是国际知名的期刊数据库&#xff0c;ei不仅收录国际知名的刊物&#xff0c;也收录了一些国内期刊&#xff0c;为方便投稿选刊&#xff0c;Elsevier官网更新了的EI Compendex期刊目录&#xff0c;那么 国内ei期刊有哪些? 经查询共有250余种期刊&#xff0c;新…

【LeetCode-面试经典150题-day23】

目录 108. 将有序数组转换为二叉搜索树 148.排序链表 427.建立四叉树 23.合并K个升序链表 108. 将有序数组转换为二叉搜索树 题意&#xff1a; 给你一个整数数组 nums &#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二…

如何实现自己在家搭建全端口P2P穿透?快解析内网穿透

对于有公网主机&#xff0c;有一定的操作能力&#xff0c;需要独立资源配置使用的&#xff0c;可以选择自行搭建内网映射服务。那么如何实现自己搭建全端口P2P穿透呢&#xff1f;下面为大家提供了不同场景下的不同方法&#xff0c;供大家使用时参考。 SSH是一种安全的远程登录…

一个方法用js生成随机双色球、大乐透

代码如下&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><s…

【C++】构造函数分类 ① ( 构造函数分类简介 | 无参构造函数 | 有参构造函数 | 拷贝构造函数 | 代码示例 - 三种类型构造函数定义与调用 )

文章目录 一、构造函数分类1、构造函数分类简介2、构造函数分类代码分析无参构造函数有参构造函数拷贝构造函数 二、代码示例 - 三种类型构造函数定义与调用 一、构造函数分类 1、构造函数分类简介 C 构造函数可以分为以下几类 : 无参构造函数 : 最简单也是默认的构造函数 , 函…

gitlab在项目中创建自己的分支的顺序操作,一整套流程

gitlab在项目中创建自己的分支的顺序操作&#xff0c;一整套流程 目录概述需求&#xff1a; 设计思路实现思路分析 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a better…

【C++】day4学习成果:仿写string类等等

1.仿照string类&#xff0c;完成myString 类 代码&#xff1a; #include <iostream> #include <cstring>using namespace std;class myString {private:char *str; //记录c风格的字符串int size; //记录字符串的实际长度public://无参构造myS…

C++学习之list的实现

在了解学习list实现之前我们首先了解一下关于迭代器的分类&#xff1a; 按功能分类&#xff1a; 正向迭代器 反向迭代器 const正向迭代器 const反向迭代器 按性质分类&#xff1a; 单向迭代器 只能 例如单链表 双向迭代器 可&#xff0c;也可-- 例如双…