当前位置: 首页 > java >正文

C语言顺序表:从零开始,解锁数据结构之门!

目录

引言

一、什么是顺序表?

二、静态顺序表

2.1、定义

2.2、代码实现

2.3、静态顺序表的缺点

三、动态顺序表

3.1、定义

3.2、代码实现

四、动态顺序表的操作

4.1、动态顺序表的初始化

4.2、尾插法

4.3、头插法

4.4、尾删法

4.5、头删法

4.6、删除指定位置的数据

4.7、指定位置之前插入数据

4.8、查找

4.9、销毁

结语


引言

“Hello, world!” 也许是你接触编程的第一句代码。但要成为一名合格的程序员,仅仅会写代码是不够的。深入理解数据结构,才能驾驭复杂的数据、设计高效的算法,构建出强大的软件。今天,我们将一起踏上数据结构的探索之旅,从最基础、也是最常用的数据结构——顺序表开始。它像一颗基石,构筑了许多高级数据结构的根基。准备好了吗?拿起你的C语言编译器,让我们一起揭开顺序表的神秘面纱!

一、什么是顺序表?

顺序表,是使用一段连续的存储空间存储数据元素的线性表。其不管在逻辑结构上还是物理结构上,数据的存储都是连续紧挨着的。

二、静态顺序表

2.1、定义

静态顺序表,顾名思义,它的大小是一定的,不可改变的。常见的静态顺序表就是数组,但是这不够严谨,因为我们只知道静态顺序表的大小,却不知道它的有效元素的个数,因此我们使用结构体来表示静态顺序表,动态顺表也是同样的。

2.2、代码实现

typedef int SLDataType;
#define N 7//静态顺序表
typedef struct SeqList
{SLDataType arr[N];  //顺序表主体int size;   //有效元素个数
}SL;

注意:

(1)对 int 重新命名为 SLDateType 是为了方便后续修改顺序表的元素类型,如果 int 要替换成 long ,只需要在第一行替换即可。同理,N也是如此。

(2)对结构体重新命名也是为了方便代码后续编写,当然,你也可以单独命名:

typedef SqeList SL;

这二者的重命名方式是等价的

2.3、静态顺序表的缺点

静态顺序表的大小是不可更改的,空间小了不够用,空间大了又浪费内存,具有非常大的局限性。而为了解决这个问题,我们可以使用动态顺序表。

三、动态顺序表

3.1、定义

动态顺序表本质和静态顺序表一样,都是一段连续的存储空间存储数据元素的线性表。不同的是,动态顺序表可以随时调整自己的大小来满足需求,比静态顺序表更加灵活

3.2、代码实现

typedef int SLDataType;typedef struct SeqLsit
{SLDataType* arr;   //顺序表主体,后面会开辟空间int size;    //顺序表的有效元素个数int capacity;  //顺序表的容量
}SL;

动态顺序表由于大小随时变化,因此,它比静态顺序表多了一个参数:容量。

四、动态顺序表的操作

4.1、动态顺序表的初始化

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

注意:初始化的方式不唯一!

4.2、尾插法

//尾插法
void SLinend(SL* ps ,SLDataType x)
{assert(ps);if (ps->size == ps->capacity){int new_capacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;//增容SLDataType* str = (SLDataType*)realloc(ps->arr,sizeof(SLDataType) * new_capacity);if (str == NULL){perror("realloc");exit(1);}ps->arr = str;ps->capacity = new_capacity;}ps->arr[ps->size++] = x;
}

首先要判断传入的 ps 是不是空指针?用assert来检查,该函数包含在 <assert.h> 中

在插入之前,需要判断该顺序表的容量是否足够?如果容量足够,则直接插入即可;如果用量不够,需要扩容。一般我们扩容选择扩容到当前容量的2倍或者3倍。扩容的代码形式有很多种,不一定非要跟笔者一致。

在插入完成后,记得要把容量和有效元素个数进行更新!

4.3、头插法

//头插法
void SLin_Head(SL*ps , SLDataType x)
{assert(ps);if (ps->size == ps->capacity){int new_capacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* str = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * new_capacity);if (str == NULL){perror("realloc");exit(1);}ps->arr = str;ps->capacity = new_capacity;}for (int i = ps->size; i > 0; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}

同样的,需要检查 ps 是否为空,然后再判断是否需要增容?可以把判断是否增容这部分代码写成一个函数,方便后续使用。

4.4、尾删法

//尾删法
void SLDete_end(SL* ps)
{assert(ps && ps->size);ps->size--;
}

除了需要判断 ps 是不是 NULL ,还需要判断 该线性表中有没有元素,没有元素还怎么删呢,对吧?

这里需要注意的是,不能使用 free,对最后一个元素进行 free 处理,这样做会语法报错!free 只能释放完整的空间!

4.5、头删法

//头删法
void SLDete_head(SL* ps)
{assert(ps && ps->size);for (int i = 0; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

用后面的元素对前一个元素进行覆盖即可,最后不要忘了有效个数的更新

4.6、删除指定位置的数据

//删除指定位置的数据
void SLDete_specify(SL* ps, int pose)
{assert(ps && ps->size);assert(pose >= 0 && pose < ps->size);for (int i = pose; i < ps->size - 1; i++){ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

指定位置后面的元素依次向前覆盖

4.7、指定位置之前插入数据

//指定位置之前插入数据
void SLin_specify_f(SL* ps, int pose, SLDataType x)
{assert(ps);assert(pose >= 0 && pose < ps->size);if (ps->size == ps->capacity){int new_capacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* tmd = (SLDataType*)realloc(ps->arr, sizeof(SLDataType) * new_capacity);if (tmd == NULL){perror("realloc");exit(1);}ps->arr = tmd;ps->capacity = new_capacity;}for (int i = ps->size; i > pose; i--){ps->arr[i] = ps->arr[i - 1];}ps->arr[pose] = x;ps->size++;
}

同样,涉及到添加数据,就需要判断是否需要增容

4.8、查找

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

4.9、销毁

void SLdestory(SL* ps)
{assert(ps);if (ps->arr)free(ps->arr);ps->arr = NULL;ps->size = ps->capacity = 0;
}

这里释放完后记得将其归空,避免野指针,同时,size和capacity也要置0

结语

恭喜你!你已经掌握了C语言顺序表的核心知识。它虽然是基础,但却是通往数据结构世界的大门。通过实践和不断思考,你会发现顺序表远不止这些。希望这篇博客能激发你对数据结构的兴趣,继续探索更深层次的知识。记住,编程的乐趣在于不断学习和挑战。现在,就拿起你的编译器,把这些知识变成你自己的吧! 下次,我们将会一起探索链表,敬请期待!

http://www.xdnf.cn/news/15025.html

相关文章:

  • 无人机三叶螺旋桨概述
  • git fetch的使用
  • OpenSearch 视频 RAG 实践
  • 【libm】 18 泛型绝对值函数 fabs(fabs.rs)
  • Canny边缘检测(cv2.Canny())
  • ACL协议:核心概念与配置要点解析
  • 从真人到数字分身:3D人脸扫描设备在高校数字人建模教学中的应用
  • 在 Mac 上使用 Git 拉取项目:完整指南
  • 【科研绘图系列】R语言探索生物多样性与地理分布的可视化之旅
  • BGP 路由优选属性(6)【Ogn】
  • 文件系统----底层架构
  • Java---IDEA
  • Redis性能基准测试
  • mac m1芯片 安装pd及win10系统
  • 【超详细】CentOS系统Docker安装与配置一键脚本(附镜像加速配置)
  • 深入了解 Vim 编辑器:从入门到精通
  • 三、C++ 的 python 绑定 pybind11
  • 【C++基础语法】
  • 如何把Arduino IDE中ESP32程序bin文件通过乐鑫flsah_download_tool工具软件下载到ESP32中
  • 【EGSR2025】材质+扩散模型+神经网络相关论文整理随笔(三)
  • SQL 索引与日志知识点详解及练习题​
  • Agent自动化与代码智能
  • HTML应用指南:利用GET请求获取全国永辉超市门店位置信息
  • 申请注册苹果iOS企业级开发者证书需要公司拥有什么规模条件
  • Spring boot整合dubbo+zookeeper
  • 《O-PAS™标准的安全方法》白皮书:为工业自动化系统筑起安全防线
  • Spring核心原理的快速入门:快速了解IoC与DI
  • [实战]调频(FM)和调幅(AM)信号生成(完整C语言实现)
  • 【C++】红黑树的底层思想 and 大厂面试常问
  • selenium跳转到新页面时如何进行定位