【数据结构】顺序表(c语言实现)(附源码)

news2024/11/15 2:20:57

🌟🌟作者主页:ephemerals__

🌟🌟所属专栏:数据结构

目录

前言

1.顺序表的概念与结构

2.顺序表的分类

3.顺序表的实现

3.1 结构定义及方法的声明

3.2 方法的实现

3.2.1 初始化

3.2.2 销毁

3.2.3 打印顺序表

3.2.4 检查空间大小,不够则增容

3.2.5 尾插

3.2.6 头插

3.2.7 尾删

3.2.8 头删

3.2.9 指定位置之前插入

3.2.10 指定位置删除 

3.2.11 查找

4.程序全部代码

总结


前言

        在我们学习顺序表之前,先引入一个概念:线性表。那么线性表是什么呢?

线性表,是n个具有相同特性的数据元素的有限序列。线性表在数据结构当中广泛使用。常见的线性表有:顺序表、链表、栈、队列、字符串......线性表在逻辑上是线性结构,也就是说数据元素就像一条线一样串联在一起,但是它的每一个数据元素的地址并不一定是连续的

了解到顺序表是线性表的一种,接下来我们进入正题,开始正式学习顺序表。

1.顺序表的概念与结构

顺序表的概念:顺序表是一段按照连续的内存地址将数据元素依次存储的数据结构。一般情况下,它的底层逻辑是数组。也就是说,顺序表的每个元素的内存地址是连续的

顺序表和数组的区别:虽然顺序表的底层结构是数组,但是我们在实现顺序表的过程中,对数组进行了封装,在数组的基础上增加了对它的一些方法,例如增删查改等操作

2.顺序表的分类

        顺序表可以分为静态顺序表动态顺序表。顾名思义,静态顺序表的大小是固定不变的。它的结构定义如下:

#define N 10

typedef int SLDataType;

//静态顺序表
typedef struct SeqList
{
	SLDataType arr[N];//固定大小的数组
	int size;//有效数据的个数
}SL;

显然,这种结构是有缺陷的。当我们需要存放的数据很多时,它的内存大小是不够的。当存放的数据过少时,又会造成空间的浪费。所以,就有了动态顺序表。动态顺序表的内存大小可以根据数据的数量发生改变。它的结构定义如下:

typedef int SLDataType;

//动态顺序表
typedef struct SeqList
{
	SLDataType* arr;//定义起始指针,后续动态开辟内存空间
	int size;//有效数据的个数
	int capacity;//数组的空间大小
}SL;

由于动态顺序表强大的灵活性和实用性,我们平时所谈到的顺序表一般都指的是动态顺序表。接下来我们在以上结构的基础上,一一实现动态顺序表的基本功能

3.顺序表的实现

3.1 结构定义及方法的声明

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

typedef int SLDataType;

//动态顺序表
typedef struct SeqList
{
	SLDataType* arr;//定义起始指针,后续动态开辟内存空间
	int size;//有效数据的个数
	int capacity;//数组的空间大小
}SL;

//初始化
void SLInit(SL* ps);

//销毁
void SLDestroy(SL* ps);

//打印顺序表
void SLPrint(SL* ps);

//检查空间大小,不够则增容
void SLCheckCapacity(SL* ps);

//尾插
void SLPushBack(SL* ps, SLDataType n);

//头插
void SLPushFront(SL* ps, SLDataType n);

//尾删
void SLPopBack(SL* ps);

//头删
void SLPopFront(SL* ps);

//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType n);

//指定位置删除数据
void SLErase(SL* ps, int pos);

//查找
void SLFind(SL* ps, SLDataType n);

以上就是关于顺序表的定义和一些方法的的声明。接下来,我们尝试一一实现这些方法。

3.2 方法的实现

3.2.1 初始化

        初始化时,我们将结构体赋一个初值就可以。代码如下:

//初始化
void SLInit(SL* ps)
{
	assert(ps);//断言一下,确保传入的不是空指针
	ps->arr = NULL;
	ps->capacity = ps->size = 0;
}

初始情况下,arr是一个空指针,结构的空间大小和数据个数都为0。

3.2.2 销毁

        销毁顺序表时,我们将arr的内存释放掉,然后将空间大小和数据个数调整尾0就好了。代码如下:

//销毁
void SLDestroy(SL* ps)
{
    assert(ps);//防止传空指针
    if (ps->arr != NULL)//防止多次释放
    {
        free(ps->arr);
        ps->arr = NULL;
    }
    ps->capacity = ps->size = 0;
}

3.2.3 打印顺序表

//打印顺序表
void SLPrint(SL* ps)
{
	assert(ps);//防止传空指针
	for (int i = 0; i < ps->size; i++)//遍历打印
	{
		printf("%d ", ps->arr[i]);
	}
	printf("\n");
}

3.2.4 检查空间大小,不够则增容

        在我们插入数据的时候,数据的总数有可能会超出顺序表的空间大小,此时我们就需要检查空间大小,如果不够就需要增容。我们将增容封装为一个函数来实现:

//检查空间大小,不够则增容
void SLCheckCapacity(SL* ps)
{
	assert(ps);
	if (ps->capacity == ps->size)//空间大小与数据个数相等则说明空间已满
	{
		int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//设定一个新空间大小,第一次增容时大小为4,之后每次以2倍的形式增容
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, NewCapacity * sizeof(SLDataType));//防止内存丢失,创建局部变量暂时接收起始地址
		if (tmp == NULL)//内存开辟失败退出程序
		{
			perror("realloc");
			exit(1);
		}
		ps->arr = tmp;//将调整好的内存赋值给arr
		ps->capacity = NewCapacity;
	}
}

3.2.5 尾插

//尾插
void SLPushBack(SL* ps, SLDataType n)
{
	assert(ps);
	SLCheckCapacity(ps);//检查空间大小
	ps->arr[ps->size++] = n;//在下标为size的位置插入元素,然后size自增
}

3.2.6 头插

        在头插的过程中,我们需要先将所有的数据全部后移一位,然后在第一个位置插入数据。

代码如下:

//头插
void SLPushFront(SL* ps, SLDataType n)
{
	assert(ps);
	SLCheckCapacity(ps);//检查空间大小
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];//所有元素后移一位
	}
	ps->arr[0] = n;//第一个位置插入数据
	ps->size++;//元素个数加1
}

3.2.7 尾删

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size);//若数据为空,则不能删除
	ps->size--;//size自减,则最后一个元素无法被访问到,相当于删除了最后一个元素
}

这里只需要将size自减,使得最后一个元素无法被访问,相当于完成了删除操作。

3.2.8 头删

        头删时,我们将第一个元素之后的所有元素向前移动一位即可。代码如下:

//头删
void SLPopFront(SL* ps)
{
	assert(ps && ps->size);//合并两个断言语句
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//整体向前移动一位,覆盖第一个元素
	}
	ps->size--;//元素个数减1
}

3.2.9 指定位置之前插入

        在我们实现指定位置插入时,需要将该位置及之后的所有元素整体向后移动一位,然后再插入元素即可。代码如下:

//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType n)//这里的参数pos是下标
{
	assert(ps && pos >= 0 && pos <= ps->size);//确保pos在合理范围内
	SLCheckCapacity(ps);
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];//将pos位置后的元素整体向后移动一位
	}
	ps->arr[pos] = n;//插入
	ps->size++;//元素个数加1
}

3.2.10 指定位置删除

        指定位置删除时,将该位置之后的元素整体向前移动一位,覆盖该元素即可。代码如下:

//指定位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps && ps->size && pos >= 0 && pos < ps->size);//确保pos在合理范围内
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//pos之后的元素整体向前移动一位,覆盖pos位置元素
	}
	ps->size--;//元素个数减1
}

3.2.11 查找

        查找元素时,我们只需要遍历顺序表,找到符合的元素即可。

//查找
void SLFind(SL* ps, SLDataType n)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)//遍历顺序表
	{
		if (ps->arr[i] == n)
		{
			return i;//匹配成功则返回对应下标
		}
	}
	return -1;//找不到返回-1
}

4.程序全部代码

        程序全部代码如下:

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

typedef int SLDataType;

//动态顺序表
typedef struct SeqList
{
	SLDataType* arr;//定义起始指针,后续动态开辟内存空间
	int size;//有效数据的个数
	int capacity;//数组的空间大小
}SL;

//初始化
void SLInit(SL* ps);

//销毁
void SLDestroy(SL* ps);

//打印顺序表
void SLPrint(SL* ps);

//检查空间大小,不够则增容
void SLCheckCapacity(SL* ps);

//尾插
void SLPushBack(SL* ps, SLDataType n);

//头插
void SLPushFront(SL* ps, SLDataType n);

//尾删
void SLPopBack(SL* ps);

//头删
void SLPopFront(SL* ps);

//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType n);

//指定位置删除数据
void SLErase(SL* ps, int pos);

//查找
void SLFind(SL* ps, SLDataType n);

//初始化
void SLInit(SL* ps)
{
	assert(ps);//断言一下,确保传入的不是空指针
	ps->arr = NULL;
	ps->capacity = ps->size = 0;
}

//销毁
void SLDestroy(SL* ps)
{
    assert(ps);//防止传空指针
    if (ps->arr != NULL)//防止多次释放
    {
        free(ps->arr);
        ps->arr = NULL;
    }
    ps->capacity = ps->size = 0;
}

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

//检查空间大小,不够则增容
void SLCheckCapacity(SL* ps)
{
	assert(ps);
	if (ps->capacity == ps->size)//空间大小与数据个数相等则说明空间已满
	{
		int NewCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//设定一个新空间大小,第一次增容时大小为4,之后每次以2倍的形式增容
		SLDataType* tmp = (SLDataType*)realloc(ps->arr, NewCapacity * sizeof(SLDataType));//防止内存丢失,创建局部变量暂时接收起始地址
		if (tmp == NULL)//内存开辟失败退出程序
		{
			perror("realloc");
			exit(1);
		}
		ps->arr = tmp;//将调整好的内存赋值给arr
		ps->capacity = NewCapacity;
	}
}

//尾插
void SLPushBack(SL* ps, SLDataType n)
{
	assert(ps);
	SLCheckCapacity(ps);//检查空间大小
	ps->arr[ps->size++] = n;//在下标为size的位置插入元素,然后size自增
}

//头插
void SLPushFront(SL* ps, SLDataType n)
{
	assert(ps);
	SLCheckCapacity(ps);//检查空间大小
	for (int i = ps->size; i > 0; i--)
	{
		ps->arr[i] = ps->arr[i - 1];//所有元素后移一位
	}
	ps->arr[0] = n;//第一个位置插入数据
	ps->size++;//元素个数加1
}

//尾删
void SLPopBack(SL* ps)
{
	assert(ps);
	assert(ps->size);//若数据为空,则不能删除
	ps->size--;//size自减,则最后一个元素无法被访问到,相当于删除了最后一个元素
}

//头删
void SLPopFront(SL* ps)
{
	assert(ps && ps->size);//合并两个断言语句
	for (int i = 0; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//整体向前移动一位,覆盖第一个元素
	}
	ps->size--;//元素个数减1
}

//指定位置之前插入数据
void SLInsert(SL* ps, int pos, SLDataType n)//这里的参数pos是下标
{
	assert(ps && pos >= 0 && pos <= ps->size);//确保pos在合理范围内
	SLCheckCapacity(ps);
	for (int i = ps->size; i > pos; i--)
	{
		ps->arr[i] = ps->arr[i - 1];//将pos位置后的元素整体向后移动一位
	}
	ps->arr[pos] = n;//插入
	ps->size++;//元素个数加1
}

//指定位置删除数据
void SLErase(SL* ps, int pos)
{
	assert(ps && ps->size && pos >= 0 && pos < ps->size);//确保pos在合理范围内
	for (int i = pos; i < ps->size - 1; i++)
	{
		ps->arr[i] = ps->arr[i + 1];//pos之后的元素整体向前移动一位,覆盖pos位置元素
	}
	ps->size--;//元素个数减1
}

//查找
void SLFind(SL* ps, SLDataType n)
{
	assert(ps);
	for (int i = 0; i < ps->size; i++)//遍历顺序表
	{
		if (ps->arr[i] == n)
		{
			return i;//匹配成功则返回对应下标
		}
	}
	return -1;//找不到返回-1
}

总结

        以上就是我们顺序表的概念及功能实现。不难发现,它的许多方法都需要遍历数组,时间复杂度为O(N),运行效率不是很高。之后博主将会介绍链表的相关知识和功能,他会弥补顺序表的一些不足。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤

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

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

相关文章

学习笔记之JAVA篇(0724)

p 方法 方法声明格式&#xff1a; [修饰符1 修饰符2 ...] 返回值类型 方法名&#xff08;形式参数列表&#xff09;{ java语句;......; } 方法调用方式 普通方法对象.方法名&#xff08;实参列表&#xff09;静态方法类名.方法名&#xff08;实参列表&#xff09; 方法的详…

MongoDB教程(二十一):MongoDB大文件存储GridFS

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言一、GridFS…

传输层协议——TCP

TCP协议 TCP全称为“传输控制协议”&#xff0c;要对数据的传输进行一个详细的控制。 特点 面向连接的可靠性字节流 TCP的协议段格式 源/目的端口&#xff1a;表示数据从哪个进程来&#xff0c;到哪个进程4位首部长度&#xff1a;表示该TCP头部有多少字节&#xff08;注意它…

汽车免拆诊断案例 | 2014 款上汽名爵 GT 车发动机无法起动

故障现象 一辆2014款上汽名爵GT车&#xff0c;搭载15S4G发动机&#xff0c;累计行驶里程约为18.4万km。该车因左前部发生碰撞事故进厂维修&#xff0c;更换损坏的部件后起动发动机&#xff0c;起动机运转有力&#xff0c;但无着机迹象。用故障检测仪检测&#xff0c;发现无法与…

昇思25天学习打卡营第23天 | CycleGAN图像风格迁移互换

昇思25天学习打卡营第23天 | CycleGAN图像风格迁移互换 文章目录 昇思25天学习打卡营第23天 | CycleGAN图像风格迁移互换CycleGAN模型模型结构循环一致损失函数 数据集数据下载创建数据集 网络构建生成器判别器损失函数和优化器前向计算梯度计算与反向传播 总结打卡 CycleGAN模…

W30-python01-Selenium Web自动化基础--百度搜索案例-chrome浏览器为例

原理图 一、下载webdriver--chrome浏览器 根据本机浏览器的版本号下载对应的webdriver版本 http://chromedriver.storage.googleapis.com/index.html 二、安装selenium库 pip install selenium -i Simple Index 三、第一个Web自动化脚本 selenium实现Web自动化的基本步骤&…

基于vue-grid-layout插件(vue版本)实现增删改查/拖拽自动排序等功能(已验证、可正常运行)

前端时间有个需求&#xff0c;需要对33&#xff08;不一定&#xff0c;也可能多行&#xff09;的卡片布局&#xff0c;进行拖拽&#xff0c;拖拽过程中自动排序&#xff0c;以下代码是基于vue2&#xff0c;可直接运行&#xff0c;报错可评论滴我 部分代码优化来自于GPT4o和Clau…

Nginx Proxy缓存

Proxy缓存 缓存类型 网页缓存 &#xff08;公网&#xff09;CDN数据库缓存 memcache redis网页缓存 nginx-proxy客户端缓存 浏览器缓存 模块 ngx_http_proxy_module 语法 缓存开关 Syntax: proxy_cache zone | off; Default: proxy_cache off; Context: http,…

C语言------指针讲解(3)

一、字符指针 在指针中&#xff0c;我们知道有一类指针类型为字符指针char*; int main() {char ch w;char* pc &ch;*pc w;return 0; } 还有一种使用方式如下&#xff1a; 上述代码中&#xff0c;本质是把hello的首字符的地址放到了pstr中。即把一个常量字符串的首字符…

CMakeList学习笔记

设置项目&#xff1a;project project(planning VERSION 1.0.0 LANGUAGES CXX) # 项目的名字 版本 1.1.0 编程语言 CXX 设置包含目录&#xff1a;include_directories、targer_include_directories 设置编译类型&#xff1a;add_executable、add_library add_executable(demo d…

VMware 上的 Debian Linux 虚拟机无法听到蓝牙耳机的声音解决方案

项目场景&#xff1a; 在Debian上安装QQ音乐&#xff0c;用来摸鱼 问题描述 在安装完QQ音乐后&#xff0c;发现虚拟机无法听到声音&#xff0c;音乐有在正常播放&#xff0c;但是蓝牙耳机没有听到任何声音&#xff1a; 原因分析&#xff1a; 感觉是虚拟机的声卡没有配置&…

【2024最新华为OD-C/D卷试题汇总】[支持在线评测] 开源项目热度排行榜(100分) - 三语言AC题解(Python/Java/Cpp)

🍭 大家好这里是清隆Coding ,一枚热爱算法的程序员 ✨ 本系列打算持续跟新华为OD-C/D卷的三语言AC题解 👏 感谢大家的订阅➕ 和 喜欢💗 🍿 最新华为OD机试D卷目录,全、新、准,题目覆盖率达 95% 以上,支持题目在线评测,专栏文章质量平均 93 分 最新华为OD机试目录…

VScode 批量操作

VScode 批量操作 批量修改 按住 alt/option 键&#xff0c; 选择需要批量操作的位置 如果是多行&#xff0c;则按住 altshift 键 可以直接操作 但是有时候比如变量命名&#xff0c;可能需要递增操作的命名 需要下载插件 Increment Selection 按照1的方法多选光标之后&am…

Qemu virtio-blk 后端驱动开发 - PureFlash对接

本文以PureFlash为例&#xff0c;介绍了如何将一个新的存储类型对接到qemu虚拟化平台下&#xff0c;为虚机提供存储能力。 关于virtio-blk以及其工作原理这里就不介绍了&#xff0c;网上有很多分析的文章。总之就是如果我们想给虚机提供一种新的存储类型&#xff08;不同于标准…

解决显存不足问题:深度学习中的 Batch Size 调整【模型训练】

解决显存不足问题&#xff1a;深度学习中的 Batch Size 调整 在深度学习训练中&#xff0c;显存不足是一个常见的问题&#xff0c;特别是在笔记本等显存有限的设备上。本文将解释什么是 Batch Size&#xff0c;为什么调整 Batch Size 可以缓解显存不足的问题&#xff0c;以及调…

大数据-48 Redis 通信协议原理RESP 事件处理机制原理 文件事件 时间事件 Reactor多路复用

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

鸿蒙开发仓颉语言【Hyperion: 一个支持自定义编解码器的TCP通信框架】组件

Hyperion: 一个支持自定义编解码器的TCP通信框架 特性 支持自定义编解码器高效的ByteBuffer实现&#xff0c;降低请求处理过程中数据拷贝自带连接池支持&#xff0c;支持连接重建、连接空闲超时易于扩展&#xff0c;可以积木式添加IoFilter处理入栈、出栈消息 组件 hyperio…

c++ 求解质因数

定义 这里先来了解几个定义&#xff08;如已了解&#xff0c;可直接看下一个板块&#xff09; 因数&#xff1a;又称为约数&#xff0c;如果整数a除以整数b&#xff08;b0&#xff09;的商正好是是整数而没有余数&#xff0c;我们就说b是a的因数 质数&#xff1a;又称为素数…

我在Vscode学Java泛型(泛型设计、擦除、通配符)

Java泛型 一、泛型 Generics的意义1.1 在没有泛型的时候&#xff0c;集合如何存储数据1.2 引入泛型的好处1.3 注意事项1.3.1 泛型不支持基本数据类型1.3.2 当泛型指定类型&#xff0c;传递数据时可传入该类及其子类类型1.3.3 如果不写泛型&#xff0c;类型默认是Object 二、泛型…

Python酷库之旅-第三方库Pandas(044)

目录 一、用法精讲 151、pandas.Series.any方法 151-1、语法 151-2、参数 151-3、功能 151-4、返回值 151-5、说明 151-6、用法 151-6-1、数据准备 151-6-2、代码示例 151-6-3、结果输出 152、pandas.Series.autocorr方法 152-1、语法 152-2、参数 152-3、功能 …