顺序表定义
顺序表是一种常见的数据结构,用于存储一组具有相同数据类型的元素,并按照线性顺序排列。在顺序表中,每个元素都有一个唯一的索引值(即我们通常所说的下标),用于标识其在表中的位置。这个索引值通常从0开始,依次递增。
顺序表有两种实现方式:静态顺序表和动态顺序表。
静态顺序表的适用场景:明确知道需要储存数据的个数。
动态顺序表的适用场景:一般情况下都适用。
PS:本文主要讲动态顺序表的实现!!!
静态顺序表:
动态顺序表:
顺序表的特点:
①随机访问:由于顺序表使用数组实现,通过索引值可以快速访问任意位置的元素。
②顺序存储:元素在物理空间上是连续存储的,因此可以直接通过内存地址计算得到元素位置,无需额外的指针来链接元素。
③固定或可动态调整大小:静态顺序表在创建时大小固定,而动态顺序表可以根据需要进行动态扩容或缩容。
④线性结构:顺序表的元素之间存在一对一的前后关系,形成线性结构。
源码
废话不多说,先贴出代码。顺序表我是用一个头文件SeqList.h和一个.cpp文件SeqList.cpp实现的。
SeqList.h
//*********************************
// 作者:suwu
// 环境: vs2019 win10
// 最后修改时间:2023/7/23 15:51
//*********************************
#pragma once
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
typedef int SLDateType;//顺序表里一个存储单元的数据类型,如需修改,只需在此把int修改为其他数据类型
// 自定义顺序表的数据结构
typedef struct SeqList
{
SLDateType* a;//顺序表数据
int size;//现有存储数据个数
int capacity;//顺序表容量
}SeqList;
// 对数据的管理:增删查改
// 函数名称:SeqListInit
// 函数功能:初始化顺序表,为顺序表申请空间,顺序表容量初始化为3个。
// 函数参数:顺序表指针ps
// 返回类型及内容:空
void SeqListInit(SeqList* ps);//初始化
// 函数名称:SeqListDestroy
// 函数功能:销毁顺序表,释放顺序表申请过的空间,并把指针置空。
// 函数参数:顺序表指针ps
// 返回类型及内容:空
void SeqListDestroy(SeqList* ps);//销毁
// 函数名称:SeqListPrint
// 函数功能:按顺序表里数据排放顺序打印数据。
// 函数参数:顺序表指针ps
// 返回类型及内容:空
void SeqListPrint(SeqList* ps);//打印
// 顺序表查找
// 函数名称:SeqListFind
// 函数功能:查找顺序表里面的数据,和x相同的第一个数据的下标将被返回。
// 函数参数:顺序表指针ps,比对数据x
// 返回类型及内容:和x相同的第一个数据的下标,如果没找到或者顺序表指针ps为空则返回-1
int SeqListFind1(SeqList* ps, SLDateType x);
// 函数名称:SeqListFind
// 函数功能:从begin下标位置开始查找顺序表里面的数据,和x相同的第一个数据的下标将被返回。
// 配合while和Erase可以把所有与x值相同的数据删除
// 函数参数:顺序表指针ps,比对数据x,开始查找下标begin
// 返回类型及内容:和x相同的第一个数据的下标,如果没找到或者顺序表指针ps为空则返回-1
int SeqListFind2(SeqList* ps, SLDateType x, int begin);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);
void SeqListPushBack(SeqList* ps, SLDateType x);//尾插,笔试可用Insert函数实现。
void SeqListPushFront(SeqList* ps, SLDateType x);//头插,笔试可用Insert函数实现。
void SeqListPopFront(SeqList* ps);//头删,笔试可用Erase函数实现。
void SeqListPopBack(SeqList* ps);//尾删,笔试可用Erase函数实现。
SeqList.cpp
//*********************************
// 作者:suwu
// 环境: vs2019 win10
// 最后修改时间:2023/7/23 15:51
//*********************************
#define _CRT_SECURE_NO_WARNINGS 1
#include "SeqList.h"
void SeqListBoost(SeqList* ps)//扩容
{
if (ps == NULL)
{
return;
}
int newcapacity = (ps->capacity == 0) ? 4 : 2 * ps->capacity;
SLDateType* arr = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));
if(arr != NULL)
{
ps->a = arr;
ps->capacity = newcapacity;
}
else
{
perror("SeqListBoost()");
}
}
void SeqListInit(SeqList* ps)//初始化
{
if (ps == NULL)
{
perror("SeqListInit()");
}
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
void SeqListDestroy(SeqList* ps)//空间释放
{
if (ps == NULL)
{
perror("SeqListDestroy()");
}
if (ps->a)
{
free(ps->a);
ps->a = NULL;
}
ps->size = 0;
ps->capacity = 0;
}
void SeqListPrint(SeqList* ps)//遍历打印
{
if (ps == NULL)
{
return;
}
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
int SeqListFind1(SeqList* ps, SLDateType x)//查找函数
{
if (ps == NULL)
{
return -1;
}
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
int SeqListFind2(SeqList* ps, SLDateType x, int begin)
{
if (ps == NULL)
{
return -1;
}
for (int i = begin; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
void SeqListInsert(SeqList* ps, int pos, SLDateType x)//任意位置插入
{
if (ps == NULL)
{
perror("SeqListInsert");
}
if (pos < 0 || pos > ps->size)//pos下标位置必须合理
{
return;
}
if (ps->size + 1 > ps->capacity)//空间不够就扩容
{
SeqListBoost(ps);
}
int i = 0;
for (i = ps->size; i > pos; i--)//从后往前,挪位置,腾出空间插入
{
ps->a[i] = ps->a[i - 1];
}
ps->a[pos] = x;//pos下标位置处插入x
ps->size++;//现有数据个数+1
}
void SeqListErase(SeqList* ps, int pos)//指定位置删除
{
if (ps == NULL)
{
return;
}
if (pos < 0 || pos >= ps->size)//pos位置必须合理
{
return;
}
int i = 0;
for (i = pos; i < ps->size - 1; i++)//从前往后,挪位置,覆盖掉删除的数据
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
void SeqListPushBack(SeqList* ps, SLDateType x)//尾插,笔试可用Insert函数实现。
{
SeqListInsert(ps, ps->size, x);
}
void SeqListPushFront(SeqList* ps, SLDateType x)//头插,笔试可用Insert函数实现。
{
SeqListInsert(ps, 0, x);
}
void SeqListPopFront(SeqList* ps)//头删,笔试可用Erase函数实现。
{
SeqListErase(ps, 0);
}
void SeqListPopBack(SeqList* ps)//尾删,笔试可用Erase函数实现。
{
SeqListErase(ps, ps->size - 1);
}
代码说明
接下来是对源码的一些细节的解释说明,方便初学者看明白,学过的可自行看源码中的注释。
SeqList.h
这个头文件是一些结构定义和函数声明。
typedef int SLDateType;//顺序表里一个存储单元的数据类型,如需修改,只需在此把int修改为其他数据类型
这个typedef的话,给原有的/自定义的数据类型另起别名具体格式是如下:
typedef 原有的/自定义的数据类型 别名
这句语句是非常必要的,特别是以后顺序表里存储的数据不是int类型,而是其他的什么double,char,或者是自定义结构体类型等。那么你整个程序很多处地方都需要修改!但你加了这一句语句你只需要把中间的int改成你想要就可以了。适用性和便捷性会提高不少。
// 自定义顺序表的数据结构
typedef struct SeqList
{
SLDateType* a;//顺序表数据
int size;//现有存储数据个数
int capacity;//顺序表容量
}SeqList;
这段代码首先定义了顺序表的结构,
数据类型为SLDateType*的储存数据的动态数组a,以及现存数据个数和顺序表容量。
其次,用 typedef 将 struct SeqList 另起别名为 SeqList。
接下来就是一些函数声明,某个函数不太懂可以去看注释。
SeqList.cpp
这个文件里面主要是一些函数实现。
#include "SeqList.h"
首先这句声明是非常必须的,连接好函数声明和函数实现。
SeqListBoost扩容
这个函数的作用是扩充顺序表的容量。
void SeqListBoost(SeqList* ps)//扩容
{
if (ps == NULL)
{
return;
}
int newcapacity = (ps->capacity == 0) ? 4 : 2 * ps->capacity;
SLDateType* arr = (SLDateType*)realloc(ps->a, newcapacity * sizeof(SLDateType));
if(arr != NULL)
{
ps->a = arr;
ps->capacity = newcapacity;
}
else
{
perror("SeqListBoost()");
}
}
realloc是一个扩容函数,不懂可自行搜索。
realloc有两种扩容方式,一种是原地扩容,一种是异地扩容。区别在于重新分配后的内存首地址是否改变。
这句语句在此的大概作用是把ps->a所拥有的空间扩大两倍,并把分配的数组首地址赋值给arr。
如果arr为空则说明扩容失败,
不为空,则扩容成功,可把arr重新赋值给ps->a,再把容量*2即可。
这里需要注意的是不可以把realloc后的地址赋值自身。
即不可以写成这样!!!!
ps->a = (SLDateType*)realloc(ps->a, 2 * ps->capacity* sizeof(SLDateType))
这样的话你无法得知是否扩容成功。realloc函数扩容失败会返回空指针。
初始化和空间释放
void SeqListInit(SeqList* ps)//初始化
{
if (ps == NULL)
{
perror("SeqListInit()");
}
ps->a = NULL;
ps->size = 0;
ps->capacity = 0;
}
SeqListInit这个函数是初始化函数,如果传入顺序表指针为空则报错。
初始化时申请空间个数可以少一点,后续可以扩容,提高空间利用率。
把顺序表结构每个数据都初始化好就行。
void SeqListDestroy(SeqList* ps)//空间释放
{
if (ps == NULL)
{
perror("SeqListDestroy()");
}
if (ps->a)
{
free(ps->a);
ps->a = NULL;
}
ps->size = 0;
ps->capacity = 0;
}
SeqListDestroy这个函数释放空间的函数,整个程序将要执行完可以执行这个函数,避免数据泄露。
释放申请空间后,记得把指针置空。
遍历打印
void SeqListPrint(SeqList* ps)//遍历打印
{
if (ps == NULL)
{
return;
}
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
SeqListPrint这个函数我想应该没什么好说的,就是把顺序表现有的数据打印一遍。
注意考虑到 接受的函数实参不能是空指针的情况 就可以了。
数据查找
int SeqListFind1(SeqList* ps, SLDateType x)//查找函数
{
if (ps == NULL)
{
return -1;
}
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
//配合while和Erase能把所有和x相同的数据都删除了
int SeqListFind2(SeqList* ps, SLDateType x, int begin)
{
if (ps == NULL)
{
return -1;
}
for (int i = begin; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
SeqListFind1这个函数的话就找跟x相同的第一个数据并返回索引值(下标)。
顺序表指针为空或者找不到就返回-1.
SeqListFind2这个函数是一个在Find1上扩展的函数,旨在配合while找到所有与x相同的数据的下标。
数据插入
任意位置插入 Insert
void SeqListInsert(SeqList* ps, int pos, SLDateType x)//任意位置插入
{
if (ps == NULL)
{
perror("SeqListInsert");
}
if (pos < 0 || pos > ps->size)//pos下标位置必须合理
{
return;
}
if (ps->size + 1 > ps->capacity)//空间不够就扩容
{
SeqListBoost(ps);
}
int i = 0;
for (i = ps->size; i > pos; i--)//从后往前,挪位置,腾出空间插入
{
ps->a[i] = ps->a[i - 1];
}
ps->a[pos] = x;//pos下标位置处插入x
ps->size++;//现有数据个数+1
}
SeqListInsert这个函数就是任意位置插入,这里需要注意的是pos下标位置必须合理。
因为后续头插尾插要用到Insert这个函数,所以得注意pos的范围不能小于0也不能大于ps->size。
这里注意挪位置那个循环,插入需要把数据都往后挪,空出一个位置方便插入。
头插
在顺序表最前面插入。可用Insert实现。
void SeqListPushFront(SeqList* ps, SLDateType x)//头插,笔试可用Insert函数实现。
{
SeqListInsert(ps, 0, x);
}
尾插
在顺序表最后面插入。可用Insert实现。
void SeqListPushBack(SeqList* ps, SLDateType x)//尾插,笔试可用Insert函数实现。
{
SeqListInsert(ps, ps->size, x);
}
数据删除
指定位置删除 Erase
void SeqListErase(SeqList* ps, int pos)//指定位置删除
{
if (ps == NULL)
{
return;
}
if (pos < 0 || pos >= ps->size)//pos位置必须合理
{
return;
}
int i = 0;
for (i = pos; i < ps->size - 1; i++)//从前往后,挪位置,覆盖掉删除的数据
{
ps->a[i] = ps->a[i + 1];
}
ps->size--;
}
pos这个函数参数接受的是要删除数据的索引值(下标),ps接受的是顺序表的指针。
如果pos大于或等于ps->size或者pos小于0,也就是说要删除的位置比现有数据量个数大,不用对数据做任何操作。
如果小于,则做覆盖操作。
头删
在顺序表最前面删除一个数据。可用Erase实现。
void SeqListPopFront(SeqList* ps)//头删,笔试可用Erase函数实现。
{
SeqListErase(ps, 0);
}
尾删
在顺序表最后面删除一个数据。可用Erase实现。
void SeqListPopBack(SeqList* ps)//尾删,笔试可用Erase函数实现。
{
SeqListErase(ps, ps->size - 1);
}
最后
写作不易,点点赞、关注和收藏。有任何疑问请在评论提出!一起进步!