数据结构之顺序表:一款优秀的顺序存储结构

🌈这里是say-fall分享,感兴趣欢迎三连与评论区留言

🔥专栏:《C语言从零开始到精通》《C语言编程实战》《数据结构与算法》

💪格言:做好你自己,你才能吸引更多人,并与他们共赢,这才是你最好的成长方式。


前言:

到目前为止C语言所有知识点我们已经学习完了,有一些不常用的知识点这里就不再继续拓展,从这篇博客开始,我们正式走入一个新的专栏:《数据结构与算法》,本篇我们就来了解一下:顺序表(本顺序表基于C语言)


文章目录

  • 前言:
  • 正文:
      1. 什么是顺序表?
      1. 顺序表分类
      • 2.1 静态顺序表
      • 2.2 动态顺序表
      1. 动态顺序表的实现
      • 3.1 头文件
      • 3.2 源文件(函数)
      • 3.3 源文件(测试)

正文:

1. 什么是顺序表?

在数据结构中,顺序表是一种线性表的存储结构,它通过连续的存储空间来存储元素,使得表中的元素在逻辑上的相邻关系在物理存储位置上也相邻。

  • 核心特点:
  1. 物理存储连续所有元素依次存放在一片连续的内存空间中,例如数组就是典型的顺序表实现。
  2. 元素按索引访问由于存储连续,可通过首地址 + 索引偏移量直接计算任意元素的地址,因此支持随机访问(时间复杂度为 O (1))。
  3. 容量固定或动态扩容
    静态顺序表:初始化时确定容量,后续不可更改(易溢出)。
    动态顺序表:容量可随元素数量增长自动扩容(如复制旧数据到新的更大空间)。

2. 顺序表分类

2.1 静态顺序表

定义:

c 复制代码
#define num 100  //num是改变数组大小的,但只能使用前改变
struct StaticSeqList
{
	int arr[num];//这里是一个定长数组
	int size;//size用来记录已有的元素个数
};

总的来说静态顺序表其实和一般数组是很类似的

2.2 动态顺序表

定义:

c 复制代码
struct seqlist
{
	sltype* arr;//这是一个指针,可以开辟空间
	int size;//记录已有元素个数
	int capacity;//用来记录开辟空间长度
};

与静态顺序表不同,动态顺序表实际上和柔性数组一样,可控制数组大小

3. 动态顺序表的实现

下面带大家写一下这个顺序表:

其中主要是包含函数源文件的写法

3.1 头文件

想要实现一个顺序表首先肯定得先定义他,这里我们在添加我们需要的头文件以后定义一个动态顺序表

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

typedef int sltype;//为数组元素类型进行重定义,方便以后修改
typedef struct seqlist
{
	sltype* arr;//这是一个指针,可以开辟空间
	int size;//记录已有元素个数
	int capacity;//用来记录开辟空间长度
}sl;//用sl这个名字对动态顺序表进行重定义

以下是我们需要进行的函数的声明,为了方便我们一般把他们放到头文件中

c 复制代码
//检查空间
void SLcheckcapacity(sl* ps);
//顺序表的初始化
void SLinit(sl* ps);
//顺序表的销毁
void SLdestory(sl* ps);
//顺序表的尾插
void SLappend(sl* ps, sltype x);
//顺序表的打印
void printsl(sl* ps);
//顺序表的头插
void SLprepend(sl* ps, sltype x);

3.2 源文件(函数)

下面就是我们的函数定义了,这里以注释和插叙的方式给大家解释

c 复制代码
#include"Seqlist.h"//将头文件放入源文件中
  • 初始化

这里是对动态顺序表的初始化,在主函数中我们首先创建一个类型为sl的叫sl1的动态顺序表,然后对它进行初始化

c 复制代码
	//初始化试验
	sl sl1;
	SLinit(&sl1);
c 复制代码
//顺序表初始化
void SLinit(sl* ps)
{
	ps->arr = NULL;
	ps->size = 0;
	ps->capacity = 0;
}

有没有人想过这里传参为什么传地址而不是传值?

  1. 实际上传值的话他会在函数内部创建一份形参(动态顺序表的拷贝)而无法实际修改真实的动态顺序表,而且创建这些形参时也会产生额外的内存占用
  2. 动态顺序表内部的成员实际上都是依靠指针来访问的,直接传地址来修改更合理
  • 销毁

这里我们先不对顺序表进行任何操作,我们先来试着销毁刚才已经初始化的顺序表

c 复制代码
//顺序表的销毁
void SLdestory(sl* ps)
{
	assert(ps != NULL);//注意这里需要检查顺序表指针是否为空,使用assert宏来控制它
	free(ps->arr);//在使用中我们会申请动态内存这个时候就需要释放内存
	ps->arr = NULL;//释放内存以后将顺序表内的舒徐全部置空或者置零方式内存泄漏
	ps->size = ps->capacity = 0;
}
  • 插入操作_尾插
c 复制代码
//顺序表的尾插
void SLappend(sl* ps ,sltype x)//这里定义时将地址传来,再将要插入的数据传入函数
{
	assert(ps != NULL);
	//插入之前,检查空间是否足够
	SLcheckcapacity(ps);//检查空间我们紧跟尾插讲解
	ps->arr[ps->size] = x;//确保没有问题以后将最后一个空间放上我们要插入的数据
	ps->size++;//不要忘记同步实际使用空间
}
  • 检查空间

检查空间大小时候我们考虑两种情况:①.还没有开辟空间;②已经开辟空间,但空间不够了需要扩容

c 复制代码
//检查空间
void SLcheckcapacity(sl* ps)
{
	assert(ps != NULL);
	if (ps->capacity == ps->size)//实际使用的空间马上要超过总空间了,这个时候扩容
	{
		sltype newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
		//定义一个新的实际空间准备使用
		//这里用条件操作符如果为开辟空间就赋值为4,如果已有空间就翻倍
		sltype* tmp = realloc(ps->arr, newcapacity * sizeof(sltype));
		//防止是空指针,我们先将开辟的新地址给一个临时变量
		if (tmp == NULL)//判断是否为空指针
		{//如果是的话则报错
			perror("realloc fail");
			exit(1);
		}
		ps->arr = tmp;//检查完以后给原来的空间真正扩容
		ps->capacity = newcapacity;//不要忘记同步实际空间数
	}
}
  • 顺序表打印
c 复制代码
//打印顺序表
void printsl(sl* ps)
{
	assert(ps != NULL);//每个函数使用前为防止传入空指针都要检查是否为空
	sltype i = 0;
	for (i = 0;i < ps->size ;i++)
	{
		printf("%d ", ps->arr[i]);//用循环的方式将顺序表内容打印出来
	}
	printf("\n");//增加换行符保证结构清楚
}
  • 头插
c 复制代码
//顺序表的头插
void SLprepend(sl* ps, sltype x)
{
	assert(ps != NULL);
	//先判断空间
	SLcheckcapacity(ps);
	//已有元素向后移动
	int i = 0;
	for (i = ps->size;i > 0 ; i--)//注意,为防止数据覆盖,这里是后面的元素先移动
	{
		ps->arr[i] = ps->arr[i-1];//最后一次移动是[0]的元素移动到[1]
	}
	ps->arr[0] = x;//将元素放入首个位置
	ps->size++;//size增加1
}
  • 头删

实际上头删和尾插是很类似的,都需要移动元素,保证顺序表的线性结构

c 复制代码
//顺序表的头删
void delete_head(sl* ps) //删除只需要传入指针地址就好了
{
	assert(ps != NULL);
	assert(ps->size);//在保证数据表不为0时候才能删除
	int i = 1;
	for (i = 1;i <= ps->size ; i++)//注意这里i一开始需要是1,因为第一个位置是0
	//头删在移动数据的过程中不用担心数据覆盖所以可以直接移动
	{
		ps->arr[i - 1] = ps->arr[i];//最后只需要移动size次就好了
	}
	ps->size--;//删除以后size--
}
  • 尾删
c 复制代码
//顺序表的尾删
void delete_tail(sl* ps)
{
	assert(ps);//这是一种上方的简便写法
	assert(ps->size);//在保证数据表不为0时候才能删除
	ps->size--;
}

3.3 源文件(测试)

c 复制代码
#include"Seqlist.h"

int main()
{
	//初始化试验
	sl sl1;
	SLinit(&sl1);
	//尾插试验
	SLappend(&sl1,5);
	printsl(&sl1);
	//头插试验
	SLprepend(&sl1, 4);
	printsl(&sl1);
	SLprepend(&sl1, 3);
	printsl(&sl1);
	SLprepend(&sl1, 2);
	printsl(&sl1);
	SLprepend(&sl1, 1);
	printsl(&sl1);
	//头删试验
	delete_head(&sl1);
	printsl(&sl1);
	//尾删试验
	delete_tail(&sl1);
	printsl(&sl1);
	//尾插试验
	SLappend(&sl1, 0);
	printsl(&sl1);
	//销毁试验
	SLdestory(&sl1);
	return 0;
}

下面是测试结果:

  • 以上就是顺序表的全部实现方法了
    完整代码放在gitee仓库,有需要请自行查看

  • 本节完...
相关推荐
yuuki2332336 小时前
【数据结构】顺序表的实现
c语言·数据结构·后端
GilgameshJSS6 小时前
STM32H743-ARM例程31-CAN
c语言·arm开发·stm32·单片机·嵌入式硬件
信奥卷王6 小时前
[GESP202506 五级] 奖品兑换
数据结构·算法
泡沫冰@6 小时前
数据结构(11)
数据结构
奶茶树6 小时前
【数据结构】二叉搜索树
数据结构·算法
小苏兮7 小时前
【数据结构】二叉搜索树
开发语言·数据结构·c++·学习·1024程序员节
晨曦(zxr_0102)7 小时前
CSP-X 2024 复赛编程题全解(B4104+B4105+B4106+B4107)
数据结构·c++·算法
无限进步_7 小时前
深入理解C语言scanf函数:从基础到高级用法完全指南
c语言·开发语言·c++·后端·算法·visual studio
Lei_3359677 小时前
[算法]十大排序
数据结构·算法·排序算法