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

C语言:顺序表(上)

C语言:顺序表(上)

1.顺序表的介绍
2.顺序表的实现

1.顺序表的介绍

线性表是n个具有相同特性的数据元素的有限序列。

线性表是一种在实际中广泛使用的数据结构,常见的线性表:顺序表、链表、栈、队列、字符串…

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

顺序表的底层结构是数组,对数组的封装,实现了常用的增删改查等接口。

顺序表分为两类:

  • 静态顺序表:使用定长数组存储元素。容易出现空间不够用、空间浪费的问题。
    在这里插入图片描述

  • 动态顺序表:空间可以按需申请。

接下来以动态顺序表为例,编程实现顺序表。

2.顺序表的实现

首先,我们打开vs2022,创建一个头文件SeqList.h,用来定义结构体和函数声明,再创建SeqList.c来编写函数,创建test.c文件来进行测试。
在这里插入图片描述
实现顺序表的过程中,我们需要realloc函数来进行开辟和调整空间,用assert进行检查,所以我们在头文件SeqList.h中应包含stdio.h、stdlib.h、assert.h。

//SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>

定义顺序表SeqList,再用typedef重命名为SL。

typedef struct SeqList
{int* arr;int size;    //有效数据个数int capacity;//空间大小(个数)
}SL;

指针arr可以指向数组、结构体、字符串等等数据,所以要把int重命名,以便后续更改arr指向的数据。

//SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;//把int重命名,方便后续更改
typedef struct SeqList
{SLDataType* arr;int size;    //有效数据个数int capacity;//空间大小(个数)
}SL;

接下来进行函数声明,我们要编写函数实现顺序表的初始化、销毁、打印、尾插/删、头插/删、定位插入/删除、查找。

//SeqList.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
typedef int SLDataType;//把int重命名,方便后续更改
typedef struct SeqList
{SLDataType* arr;int size;    //有效数据个数int capacity;//空间大小(个数)
}SL;
void SLInit(SL* ps);//顺序表初始化void SLDestroy(SL* ps);//顺序表销毁void SLPrint(SL s);//顺序表打印void SLPushBack(SL* ps, SLDataType x);//尾插void SLPushFront(SL* ps, SLDataType x);//头插void SLPopBack(SL* ps);//尾删void SLPopFront(SL* ps);//头删void SLInsert(SL* ps, int pos, SLDataType x);//定位插入void SLErase(SL* ps, int pos);//定位删除int SLFind(SL* ps, SLDataType x);//查找

顺序表初始化:指针ps指向顺序表,把arr先置为NULL,有效数据的个数size为0,空间大小capacity为0。

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

顺序表销毁:需要判断顺序表是否为空,若为空则不需要销毁,若不为空则先用free释放arr指向的空间,再把size、capacity置为0。

void SLDestroy(SL* ps)//顺序表销毁
{if (ps->arr)//若ps->arr为NULL,不再free,若不为NULL,则执行freefree(ps->arr);ps->arr = NULL;ps->size = ps->capacity = 0;
}

在创建顺序表之后,我们通过头插、尾插、定位插入的方式往顺序表中插入数据,但在插入之前,我们需要检查arr指向的空间是否足够,不够则用realloc函数增加空间。

我们先判断size与capacity是否相等,若相等,则说明空间不足,如图所示:
在这里插入图片描述
size与capacity不相等,则以原空间大小的2倍增加空间。

void SLCheckCapacity(SL* ps)//检查插入前空间是否足够
{if (ps->capacity == ps->size)//空间大小和有效数据个数一致,则空间不足{int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* t = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));//要申请多大空间if (t == NULL)//若空间申请失败{perror("realloc fail !");exit(1);//退出程序}ps->arr = t;//空间申请成功ps->capacity = newCapacity;//记录新的空间大小}
}

尾插:尾插函数需要参数指向顺序表的指针ps和插入的数据x,先用assert检查ps是否为NULL,再通过SLCheckCapacity函数检查空间是否足够,若不足则增加空间,在插入数据之后,还要让size加1,记录新的有效数据个数。
在这里插入图片描述

void SLPushBack(SL* ps, SLDataType x)//尾插
{assert(ps);SLCheckCapacity(ps);//检查插入前空间是否足够ps->arr[ps->size] = x;ps->size++;
}

头插:头插与尾插类似,头插还需要循环把数据整体向后挪动一位。

void SLPushFront(SL* ps, SLDataType x)//头插
{assert(ps);SLCheckCapacity(ps);//检查插入前空间是否足够for (int i = ps->size;i > 0;i--)//让顺序表中已有的数据整体往后挪一位{                               //从最后一位开始挪,避免覆盖   ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}

顺序表打印函数:

void SLPrint(SL s)//打印
{for (int i = 0;i < s.size;i++)printf("%d ", s.arr[i]);printf("\n");
}

尾删:只要size减1,让原本最后一个数据无法被访问,就实现了尾删。

void SLPopBack(SL* ps)//尾删
{assert(ps);assert(ps->size);//顺序表不为空--ps->size;
}

在这里插入图片描述
头删:与尾删类似,头删还要数据整体向前挪一位,且从第二位数据开始挪,通过覆盖第一位数据实现头删。

void SLPopFront(SL* ps)//头删
{assert(ps);assert(ps->size);for (int i = 0;i < ps->size - 1;i++)//数据整体往前挪一位{                                   //从第二位往前挪,覆盖第一位实现删除ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

定位插入:类似头插、尾插,但多了一个参数pos,pos为数据插入后的下标,所以0<=pos<=size,在插入前,下标为pos及大于pos的数据往后挪一位。

void SLInsert(SL* ps, int pos, SLDataType x)//定位插入,插入后下标为pos
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);for (int i = ps->size;i > pos;i--)//pos及之后的数据整体往后挪一位{ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;ps->size++;
}

定位删除:pos为下标,显然0<=pos<size,下标大于pos的数据往前挪一位,通过arr[pos+1]把arr[pos]覆盖掉来实现定位删除。

void SLErase(SL* ps, int pos)//定位删除
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos;i < ps->size-1;i++)//pos之后数据往前挪一位{                                   //arr[pos+1]覆盖掉arr[pos]ps->arr[i] = ps->arr[i + 1];}ps->size--;
}

查找:SLFind函数的参数x为要查找的数据,遍历整个顺序表,找到返回下标,找不到返回-1。

int SLFind(SL* ps, SLDataType x)//查找
{assert(ps);for (int i = 0;i < ps->size;i++){if (ps->arr[i] == x)return i;//找到了,返回下标}return -1;//未找到
}

在SeqList.c文件中的代码如下:

//SeqList.c
#include"SeqList.h"
void SLInit(SL* ps)//顺序表初始化
{ps->arr = NULL;ps->size = ps->capacity = 0;
}
void SLDestroy(SL* ps)//顺序表销毁
{if (ps->arr)//若ps->arr为NULL,不再free,若不为NULL,则执行freefree(ps->arr);ps->arr = NULL;ps->size = ps->capacity = 0;
}
void SLCheckCapacity(SL* ps)//检查插入前空间是否足够
{if (ps->capacity == ps->size)//空间大小和有效数据个数一致,则空间不足{int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;SLDataType* t = (SLDataType*)realloc(ps->arr, newCapacity * sizeof(SLDataType));//要申请多大空间if (t == NULL)//若空间申请失败{perror("realloc fail !");exit(1);//退出程序}ps->arr = t;//空间申请成功ps->capacity = newCapacity;}
}
void SLPushBack(SL* ps, SLDataType x)//尾插
{assert(ps);SLCheckCapacity(ps);//检查插入前空间是否足够ps->arr[ps->size] = x;ps->size++;
}
void SLPushFront(SL* ps, SLDataType x)//头插
{assert(ps);SLCheckCapacity(ps);//检查插入前空间是否足够for (int i = ps->size;i > 0;i--)//让顺序表中已有的数据整体往后挪一位{                               //从最后一位开始挪,避免覆盖   ps->arr[i] = ps->arr[i - 1];}ps->arr[0] = x;ps->size++;
}
void SLPrint(SL s)//打印
{for (int i = 0;i < s.size;i++)printf("%d ", s.arr[i]);printf("\n");
}
void SLPopBack(SL* ps)//尾删
{assert(ps);assert(ps->size);//顺序表不为空--ps->size;
}
void SLPopFront(SL* ps)//头删
{assert(ps);assert(ps->size);for (int i = 0;i < ps->size - 1;i++)//数据整体往前挪一位{                                   //从第二位往前挪,覆盖第一位实现删除ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
void SLInsert(SL* ps, int pos, SLDataType x)//定位插入,插入后下标为pos
{assert(ps);assert(pos >= 0 && pos <= ps->size);SLCheckCapacity(ps);for (int i = ps->size;i > pos;i--)//pos及之后的数据整体往后挪一位{ps->arr[i] = ps->arr[i - 1];}ps->arr[pos] = x;ps->size++;
}
void SLErase(SL* ps, int pos)//定位删除
{assert(ps);assert(pos >= 0 && pos < ps->size);for (int i = pos;i < ps->size-1;i++)//pos之后数据往前挪一位{                                   //arr[pos+1]覆盖掉arr[pos]ps->arr[i] = ps->arr[i + 1];}ps->size--;
}
int SLFind(SL* ps, SLDataType x)//查找
{assert(ps);for (int i = 0;i < ps->size;i++){if (ps->arr[i] == x)return i;//找到了,返回下标}return -1;//未找到
}

最后,我们就可以在test.c文件中做测试了,例如测试头插和尾插:

#include"SeqList.h"
void test1()
{SL s;SL* ps = &s;SLInit(ps);SLPushFront(ps, 1);SLPushBack(ps,2);SLPushBack(ps, 3);SLPushBack(ps, 4);SLPrint(s);SLDestroy(ps);
}
int main()
{test1();return 0;
}

在这里插入图片描述

拙作一篇,望诸位同道不吝斧正。

http://www.dtcms.com/a/300512.html

相关文章:

  • K8S 九 安全认证 TLS
  • 关于西门子博图基本指令的应用区别
  • VScode 支持 QNX 源码跳转
  • 【Python系列】从内存分析到性能剖析
  • Mysql 二进制安装常见问题
  • 2025年Solar应急响应公益月赛-7月wp
  • mac neo4j install verifcation
  • 论文阅读-IGEV
  • SecureCRT连接密钥交换失败
  • 基于LNMP架构的分布式个人博客搭建
  • 总结和对比Unity中的三种主要抗锯齿技术:FXAA、SMAA和TAA
  • 搭建DM数据守护集群
  • Java 代理机制详解:从静态代理到动态代理,彻底掌握代理模式的原理与实战
  • 【服务器与部署 30】Python内存优化实战:从内存泄漏到性能提升的完整解决方案
  • VLA-视觉语言动作模型
  • mac配置多版本jdk
  • 四、搭建springCloudAlibaba2021.1版本分布式微服务-加入openFeign远程调用和sentinel流量控制
  • git stash 命令详解
  • Python 程序设计讲义(24):循环结构——循环后处理 while ... as 与 for...as
  • 大模型算法面试笔记——常用优化器SGD,Momentum,Adagrad,RMSProp,Adam
  • 算法思维进阶 力扣 300.最长递增子序列 暴力搜索 记忆化搜索 DFS 动态规划 C++详细算法解析 每日一题
  • 用KNN实现手写数字识别:基于 OpenCV 和 scikit-learn 的实战教学 (超级超级超级简单)
  • Torchv Unstrustured 文档解析库
  • Mac配置本地邮件
  • 【Qt开发】信号与槽(二)-> 信号和槽的使用
  • Web Worker:解锁浏览器多线程,提升前端性能与体验
  • 29.【.NET8 实战--孢子记账--从单体到微服务--转向微服务】--单体转微服务--用户配置服务
  • 七、搭建springCloudAlibaba2021.1版本分布式微服务-skywalking9.0链路追踪
  • 重生之我在暑假学习微服务第二天《MybatisPlus-下篇》
  • MCP + LLM + Agent 8大架构:Agent能力、系统架构及技术实践