数据结构与算法之顺序表详解

news2025/1/13 10:09:02

标题:猜数字小游戏

作者:@Ggggggtm

寄语:与其忙着诉苦,不如低头赶路,奋路前行,终将遇到一番好风景

文章目录:

一、顺序表的概念与结构

1、线性表的解释

2、顺序表概念解释

二、顺序表的思路及代码实现详解

1、静态顺序表的实现

2、动态顺序表思路及代码实现

2、1 动态顺序表的整体思路

2、2 定义结构体的实现

2、3 初始化结构体

2、4 结构体打印 

2、5 检查数组容量

2、6 头插

2、7 尾插

2、8 头删

2、9 尾删

2、10 任意删除

2、11 任意插入

2、12 空间释放

 三、顺序表代码整合

SeqList.h

SeqList.c

test.c


 

一、顺序表的概念与结构

1、线性表的解释

  首先我们在这里引入线性表的概念。线性表是n个具有相同特性的数据元素的有限序列。线性表是一种在实际中广泛使用的数据结构。

  常见的线性表:顺序表、链表、栈、队列、字符串……

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

  顺序表就是线性表的一种,我们在这里详细解释一下顺序表的实现,后续我们会更新链表等内容。

2、顺序表概念解释

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

   而顺序表一般可分为:

  1. 静态顺序表:使用定长数组存储。
  2. 动态顺序表:使用动态开辟的数组存储。 

二、顺序表的思路及代码实现详解

1、静态顺序表的实现

  我们先想出来一个静态表整体的模板思路: 

  1. 定义一个结构体,该结构体包含一个可以存放数据的数组和记录数组中有效数字的变量。
  2. 初始化结构体。
  3. 打印结构体。
  4. 头插。
  5. 尾插。
  6. 头删。
  7. 尾删。
  8. 任意位置插入。
  9. 任意位置删除。

   这里需要有一点注意的是,我们在定义结构体中的数组时,我们可以用typedef进行变量名简化,这也方便我们后期更改存储类型的时候直接更改typedef处就行。同时我们会想到数组的大小需要define定义一个宏,这样大大提高了代码后期的可维护性。

  但是我们仔细想一下,假如我们存储的数据满了,我们想要继续存储的话还要找到源码进行更改大小 。每次存储满了,都要更改。那是不是太麻烦了,且效率很低。这时候我们就联想到了动态的顺序表,可以自动开辟空间,从而大大提高效率。

  这里我就给出大家静态顺序表定义及接口的代码,不再详细解释接口的实现了。我们这里详细解释一下动态顺序表的解释。静态顺序表接口的实现与动态顺序表接口实现大同小异,可参考动态顺序表接口的详解。

  代码如下:

#define MAX_SIZE 10
typedef int SQDataType;
typedef struct SeqList
{
	SQDataType a[MAX_SIZE];
	int size;
}SL;
//typedef struct SeqList SL;

typedef struct SeqList SL;
//初始化结构体
void SeqListInit(SL* ps);
//打印
void SeqListPrint(SL s);
//尾插
void SeqListPushBack(SL* ps, SQDataType x);
//尾删
void SeqListPopBack(SL* ps);
//头插
void SeqListPushFrint(SL* ps, SQDataType x);
//头删
void SeqListPopFrint(SL* ps);
//查找位置
int SeqListFind(SL s, SQDataType x);
//任意插入
void SeqListInsert(SL* ps, int pos, SQDataType x);
//任意删
void SeqListErase(SL* ps, int pos);

2、动态顺序表思路及代码实现

2、1 动态顺序表的整体思路

  动态顺序表的思路与静态大致相同,但也有所不同,我来给大家详细解释一下。我们先看动态顺序表的整体思路模板:

  1. 定义一个结构体,该结构体包含一个可以存放数据的动态数组和记录数组中有效数据的变量,两外还需要一个变量记录当前数组的大小。
  2. 初始化结构体。
  3. 打印结构体。
  4. 检查数组容量
  5. 头插。
  6. 尾插。
  7. 头删。
  8. 尾删。
  9. 任意位置插入。
  10. 任意位置删除。
  11. 释放动态数组空间

  我们上面提到了动态的数组,需要用malloc或realloc动态开辟空间。由于是动态开辟的,我们这里多了一项释放动态开辟的空间。注意,记录数组的有效数据和数组大小并不相同。有效数据是已经存储的数据个数 ,而数组大小是指最能够存储数组的个数。我们为什么要记录数组的大小呢?这里是用来判断是否存储满了,满了话要开辟空间。

  我们来详细看一下每个接口的实现。

2、2 定义结构体的实现

  在定义结构体时,我们可以用typedef进行数组类型简化,同时方便我们后期更改存储类型的时候直接更改typedef处即可。同时我们也用typedef进行结构体类型简化,方便我们以后编辑代码。我们来看一下代码的实现:

typedef int SQDataType;
struct SeqList
{
	SQDataType* a;
	int size;
	int capacity;
};
typedef struct SeqList SL;

   通过上面的代码我们可以发现,当我们不想存储int型数据时,我们只需把‘typedef int SQDataType’改为‘typedef double SQDataType’即可。极大的提高了代码的维护性。

2、3 初始化结构体

  我们初始化结构体时,可以先将数组置空,我们后期插入数据时可再开辟空间。同时当然有效数据和数组大小都要初始化成零。我们看代码的实现。 

void SeqListInit(SL* ps)
{
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

  我们这里是改变了结构体的内容,所以需要传地址,用指针变量来接收。 

2、4 结构体打印 

  结构体打印方便我们观察对动态数组的操作。打印的时数组的有效数据的内容。我们来看代码的实现。

void SeqListPrint(SL s)
{
	int i = 0;
	for (i = 0; i < s.size; i++)
	{
		printf("%d ", s.a[i]);
	}
	printf("\n");
}

2、5 检查数组容量

  我们仔细想一想,是不是在插入每个数据之前都要检查数组是否已经满了。如果满了,则需要增容。如果没有满,就插入数据即可。在这里我们需要实现头插、尾插、任意插入三个接口,所以我们就把检查数组容量单独分装一个函数,这样提高代码的简洁性。我们看一下代码的实现。

void SQLCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SQDataType* tmp =(SQDataType*)realloc(ps->a, sizeof(SQDataType) * newcapacity);
		if (tmp == NULL)
		{
			printf("realloc failed\n");
			exit(-1);
		}
		ps->capacity = newcapacity;
		ps->a = tmp;
	}
	
}

  当我们检查增容时,我们还要判断一下之前的数组大小是否为零,如果是零的话,我们要给其赋一个值。因为我们刚开始初始化数组的时候把数组指针置空了。 在动态顺序表中我们增容一般会扩大到原来的2倍。

2、6 头插

  在插入之前要判断数组是否已经满了。头插的思想就是把数组的内容整体后移一位,我们把要插入的数据放在第一位。我们结合着代码一起理解。 

void SeqListPushFrint(SL* ps, SQDataType x)
{
	SQLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end+1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}

2、7 尾插

  同样, 在插入之前要判断数组是否已经满了。尾插的思想很简单。就是直接在数组尾部插入一个数据即可。我们看一下代码的实现。

void SeqListPushBack(SL* ps, SQDataType x)
{
	SQLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}

2、8 头删

   删除时我们也有要注意的一点,就是检查数组中是否有元素给我们删除。头删的思想就是除去数组的第一个元素,我们将后面的元素整体向前移动一位,将第一位给覆盖了。我们来看代码。  

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

2、9 尾删

  同样,在尾删之前, 我们要检查数组中是否有元素给我们删除。尾删的思想十分简单,就是把数组的有效数据减一即可。我们看一下代码的实现。

void SeqListPopBack(SL* ps)
{
	assert(ps->size > 0);
	ps->size--;
}

2、10 任意删除

    在任意删除时,我们首先要判断删除的位置是否合理,不能违背顺序表的规则。同样,在尾删之前, 我们要检查数组中是否有元素给我们删除。任意删除就是我们指出删除位置的下标进行删除。当然,我们想要删除数组中指定元素时,我们可以先查出元素下标在进行删除。这个相对来说较复杂一点,我们结合着代码理解一下。

//查找位置
int SeqListFind(SL s, SQDataType x)
{
	int i = 0;
	for (i = 0; i < s.size; i++)
	{
		if (s.a[i] == x)
		{
			return i;
		}
	}
	return -1;
}
void SeqListErase(SL* ps, int pos)
{
	assert(pos >= 0 && pos < ps->size);
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}

2、11 任意插入

  在任意插入时时,我们也要判断插入的位置是否合理,不能违背顺序表的规则。插入时,我们不能忘记检查数组是否满了。任意插入的思想与任意删除的思想基本相同。任意插入的思想就是在我们指出删除位置的下标进行插入。我们看一下代码实现。

void SeqListInsert(SL* ps, int pos, SQDataType x)
{
	assert(pos >= 0 && pos <= ps->size);
	SQLCheckCapacity(ps);
	int end = ps->size-1;
	while (end >= pos)
	{
		ps->a[end+1] = ps->a[end];
		end--;
	}
	ps->a[pos] = x;
	ps->size++;
}

2、12 空间释放

  由于我们的数组是动态开辟的,所以当我们不用时,我们要及时释放掉动态开辟的空间,避免内存泄漏。同时我们要把数组指针再次置空,避免产生野指针。我们看代码实现。 

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

 三、顺序表代码整合

  由于代码量相对来说有一点多,所以我们就将函数的声明的定义分开,这样有利于提高代码的可读性,同时会保持一个良好的思路,且方便编写代码。

  我们将函数的声明放在单独的一个SeqList.h的头文件,函数的实现放在一个单独的SeqList.c源文件,函数的主方法及调用放在另一个单独的test.c源文件。

SeqList.h

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

typedef int SQDataType;
struct SeqList
{
	SQDataType* a;
	int size;
	int capacity;
};

typedef struct SeqList SL;
//初始化结构体
void SeqListInit(SL* ps);
//打印
void SeqListPrint(SL s);
//尾插
void SeqListPushBack(SL* ps, SQDataType x);
//尾删
void SeqListPopBack(SL* ps);
//头插
void SeqListPushFrint(SL* ps, SQDataType x);
//头删
void SeqListPopFrint(SL* ps);
//查找位置
int SeqListFind(SL s, SQDataType x);
//任意插入
void SeqListInsert(SL* ps, int pos, SQDataType x);
//任意删
void SeqListErase(SL* ps, int pos);
//销毁空间
void SeqListDestory(SL* ps);

SeqList.c

#include"SeqList.h"

//初始化结构体
void SeqListInit(SL* ps)
{
	ps->a = NULL;
	ps->size = 0;
	ps->capacity = 0;
}
//打印
void SeqListPrint(SL s)
{
	int i = 0;
	for (i = 0; i < s.size; i++)
	{
		printf("%d ", s.a[i]);
	}
	printf("\n");
}
//查容增容
void SQLCheckCapacity(SL* ps)
{
	if (ps->size == ps->capacity)
	{
		int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		SQDataType* tmp =(SQDataType*)realloc(ps->a, sizeof(SQDataType) * newcapacity);
		if (tmp == NULL)
		{
			printf("realloc failed\n");
			exit(-1);
		}
		ps->capacity = newcapacity;
		ps->a = tmp;
	}
	
}
//尾插
void SeqListPushBack(SL* ps, SQDataType x)
{
	SQLCheckCapacity(ps);
	ps->a[ps->size] = x;
	ps->size++;
}
//尾删
void SeqListPopBack(SL* ps)
{
	assert(ps->size > 0);
	ps->size--;
}
//头插
void SeqListPushFrint(SL* ps, SQDataType x)
{
	SQLCheckCapacity(ps);
	int end = ps->size - 1;
	while (end >= 0)
	{
		ps->a[end+1] = ps->a[end];
		end--;
	}
	ps->a[0] = x;
	ps->size++;
}
//头删
void SeqListPopFrint(SL* ps)
{
	assert(ps->size > 0);
	int i = 0;
	for (i = 0; i < ps->size - 1; i++)
	{
		ps->a[i] = ps->a[i + 1];
	}
	ps->size--;
}
//查找位置
int SeqListFind(SL s, SQDataType x)
{
	int i = 0;
	for (i = 0; i < s.size; i++)
	{
		if (s.a[i] == x)
		{
			return i;
		}
	}
	return -1;
}
//任意插——在下标为pos的位置插入数据
void SeqListInsert(SL* ps, int pos, SQDataType x)
{
	assert(pos >= 0 && pos <= ps->size);
	SQLCheckCapacity(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 SeqListErase(SL* ps, int pos)
{
	assert(pos >= 0 && pos < ps->size);
	int begin = pos + 1;
	while (begin < ps->size)
	{
		ps->a[begin - 1] = ps->a[begin];
		begin++;
	}
	ps->size--;
}
//销毁空间
void SeqListDestory(SL* ps)
{
	free(ps->a);
	ps->a = NULL;
	ps->capacity = ps->size = 0;
}

test.c

#include"SeqList.h"

void test()
{
	SL s1;
	SeqListInit(&s1);
	SeqListPushBack(&s1, 1);
	SeqListPushFrint(&s1, 1);
	SeqListPushFrint(&s1, 2);
	SeqListPushFrint(&s1, 3);
	SeqListPushFrint(&s1, 4);
	SeqListPushBack(&s1, 5);
	SeqListPrint(s1);
	SeqListPopFrint(&s1);
	SeqListPrint(s1);
	int pos = SeqListFind(s1, 1);
	SeqListInsert(&s1, pos, 10);
	SeqListInsert(&s1, 0, 20);
	SeqListPrint(s1);
	SeqListErase(&s1, 0);
	SeqListPrint(s1);
	SeqListDestory(&s1);
}
int main()
{
	test();
	return 0;
}

  以上就是顺序表的所有内容啦,希望本篇文章会对你有所帮助,感谢阅读。

  后期会持续更新链表哦ovo~。 

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

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

相关文章

ERP系统三种部署方式的区别

ERP系统被认为是一种 "企业应用程序"&#xff0c;指的是为满足企业的软件需求和提高业务绩效而设计的软件。今天有许多不同的ERP系统可供使用&#xff0c;其范围很广&#xff0c;取决于企业的规模、功能和需求。ERP系统的类型根据其部署方式划分&#xff0c;包括云ER…

二、Redis分布式锁

一、什么是分布式锁 分布式锁是一种跨进程的&#xff0c;跨机器节点的一种互斥锁。保证在多个机器节点对共享资源访问的一个排他性。 分布式与单机情况下最大的不同在于分布式锁是多进程的而单机锁是单进程多线程的。 二、为什么需要分布式锁 与分布式锁相对就的是单机锁&…

BHQ-1 amine,1308657-79-5,BHQ染料通过FRET和静态猝灭的组合工作

英文名称&#xff1a;BHQ-1 amine 中文名称&#xff1a;BHQ-1 氨基 CAS&#xff1a;1308657-79-5 外观&#xff1a;深紫色粉末 分子式&#xff1a;C25H29N7O3 分子量&#xff1a;475.55 结构式&#xff1a; 溶解性&#xff1a;溶于大部分有机溶剂&#xff0c;溶于水 储存…

luffy-(13)

内容概览 支付宝支付介绍支付宝支付二次封装订单相关表设计生成订单接口支付前端支付宝回调接口 支付宝支付介绍 """ 项目中有需要在线支付功能,可以使用支付宝支付(沙箱环境)微信支付(需要有备案过的域名)云闪付我们的项目以支付宝支付为例支付流程使用官方…

【动手学深度学习】关于“softmax回归的简单实现”报错的解决办法(含源代码)

目录&#xff1a;关于“softmax回归的简单实现”报错的解决办法一、前言二、实现步骤2.1 导包2.2 初始化模型参数2.3 重新审视Softmax的实现2.4 优化算法2.5 训练2.6 源代码三、问题出现四、问题的解决五、再跑代码六、改正后的函数源代码一、前言 在之前的学习中&#xff0c;…

题库系统(公众号免费调用)

题库系统(公众号免费调用) 本平台优点&#xff1a; 多题库查题、独立后台、响应速度快、全网平台可查、功能最全&#xff01; 1.想要给自己的公众号获得查题接口&#xff0c;只需要两步&#xff01; 2.题库&#xff1a; 查题校园题库&#xff1a;查题校园题库后台&#xff0…

arthas 源码构建

arthas 源码构建 git下载代码 git clone https://github.com/alibaba/arthas.git 若github被墙&#xff0c;可以在gitee搜索下载 maven clean 可以在项目目录执行 mvn clean &#xff0c; ide可以执行界面执行 maven package 可以在项目目录执行mvn package 问题记录 ja…

四肽Suc-AAPD-对硝基苯胺,165174-58-3

粒酶B底物succ - aapd - pna。也被ICE劈开了。 编号: 177581中文名称: 四肽Suc-Ala-Ala-Pro-Asp-对硝基苯胺CAS号: 165174-58-3单字母: Suc-AAPD-pNA三字母: Suc-Ala-Ala-Pro-Asp-pNA氨基酸个数: 4分子式: C25H32O11N6平均分子量: 592.56精确分子量: 592.21等电点(PI): -pH7.0时…

新进场的獴哥健康、至真健康们,讲不出互联网医疗的新故事

文丨智能相对论 作者丨沈浪 曾几何时&#xff0c;互联网医疗风靡一时&#xff0c;现如今潮水退去&#xff0c;当市场回归理性&#xff0c;赛道竞争趋于同质化&#xff0c;一批互联网医疗企业正在试图通过讲好新故事&#xff0c;来拉开品牌与品牌之间的商业差距&#xff0c;寻…

ArcGIS Pro 转换Smart3D生成的倾斜3D模型数据osgb——创建集成网格场景图层包

最近在做Arcgis 批处理的一些工作&#xff0c;然后再学习Python的同时&#xff0c;偶然觉得arcgis Pro是个好东西呢&#xff1f;然后结合近期的Smart3D倾斜3D模型数据&#xff0c;是否可以在arcgis里查看呢&#xff1f;带着这样的疑问和好奇&#xff0c;开始了arcgis Pro的学习…

【408专项篇】C语言笔记-第四章(选择与循环)

第四章&#xff1a;选择、循环 第一节&#xff1a;选择if-else 1. 关系表达式与逻辑表达式 if-else的判断条件结果还是真与假&#xff0c;即1或0&#xff0c;一般是关系表达式或逻辑表达式。 算术运算符的优先级高于关系运算符&#xff0c;关系运算符的优先级高于逻辑与和逻…

Webpack 5 超详细解读(四)

31.proxy 代理设置 为什么开发阶段需要设置代理,在开发阶段,我们需要请求后端接口,但是一般后端接口地址和我们本地的不在同一个服务中提供,这时进行访问就会存在跨域的问题,所以我们需要对我们的请求进行转啊操作。模拟跨域请求代码如下: https://api.github.com/users…

高项 沟通管理论文

3个过程&#xff1a; 1&#xff0c;规划沟通管理&#xff1a;根据干系人的信息需要和要求及组织的可用资产情况&#xff0c;制订合适的项目沟通方式和计划的过程。 2&#xff0c;管理沟通&#xff1a;根据沟通管理计划,生成、收集、分发、储存、检索及最终处置项目信息的过程…

Spring boot 实践Rabbitmq消息防丢失

之前看很多网上大佬的防丢失的文章&#xff0c;文章中理论知识偏多&#xff0c;所以自己想着实践一下&#xff0c;实践过程中也踩了一些坑&#xff0c;因此写出了这篇文章。如果文章有误人子弟的地方&#xff0c;望在评论区指出。 导致消息出现丢失的原因 发送时失败&#xff…

295348-87-7,AF 594 Succinimidyl Ester可用于成像和流式细胞分析

理论分析&#xff1a; 中文名&#xff1a;AF 594活性酯 英文名&#xff1a;AF 594 Succinimidyl Ester&#xff0c;Alexa Fluor 594 NHS Ester&#xff0c;AF 594 NHS Ester CAS号&#xff1a;295348-87-7 化学式&#xff1a;C39H37N3O13S2 分子量&#xff1a;819.85 ex/em : 5…

招投标业务总结

最近接了一个招标投标的项目&#xff0c;开发完成后&#xff0c;整个招投标的流程也就理清楚了&#xff0c;简介一下业务过程。业务主流程可以分为3个阶段: 招标方建立招标项目发布招标公告投标人参与竞标招标方开标评选公示 系统可以划分为两套&#xff0c;一个是给招标方使…

若依框架解读(微服务版)——2.模块间的调用逻辑(ruoyi-api模块)(OpenFeign)

模块之间的关系 我们可以了解到一共有这么多服务&#xff0c;我们先启动这三个服务 其中rouyi–api模块是远程调用也就是提取出来的openfeign的接口 ruoyi–commom是通用工具模块 其他几个都是独立的服务 ruoyi-api模块 api模块当中有几个提取出来的OpenFeign的接口 分别为文件…

华为机试 - ABR 车路协同场景

目录 题目描述 输入描述 输出描述 用例 题目解析 算法源码 题目描述 数轴有两个点的序列 A{A1&#xff0c; A2, …, Am}和 B{B1, B2, ..., Bn}&#xff0c; Ai 和 Bj 均为正整数&#xff0c; A、 B 已经从小到大排好序&#xff0c; A、 B 均肯定不为空&#xff0c; 给定…

大数据培训课程Reduce Join案例实操

Reduce Join案例实操 1&#xff0e;需求 表4-4 订单数据表t_order idpidamount100101110020221003033100401410050251006036 表4-5 商品信息表t_product pidpname01小米02华为03格力将商品信息表中数据根据商品pid合并到订单数据表中。 表4-6 最终数据形式 idpnameamount…

2022我的前端面试总结

Webpack Proxy工作原理&#xff1f;为什么能解决跨域 1. 是什么 webpack proxy&#xff0c;即webpack提供的代理服务 基本行为就是接收客户端发送的请求后转发给其他服务器 其目的是为了便于开发者在开发模式下解决跨域问题&#xff08;浏览器安全策略限制&#xff09; 想…