目录
什么是顺序表?
顺序表的实现:
顺序表的初始化:
顺序表的头插和尾插:
顺序表的头删和尾删:
顺序表的插入与删除:
顺序表的打印与查找:
SeqList.h头文件的所有声明:
VS2019全部代码截图:
SeqList.h
SeqList.c
test.c
哟呼!各位没有血缘关系和有血缘关系和不知道有没有血缘关系的亲朋好友们大家好吖~
今天给大家带来的食品是上等的熟食,C语言顺序表的详解。
顺序表,顾名思义,其中的元素不论是逻辑结构上还是物理结构上都是相邻存储的,在C语言中我们常用的数组就是一个顺序表,在内存之中他是线性存储,在逻辑上它也是线性的。
我们这次使用结构体和动态数组来实现顺序表,并编写出顺序表的:增删改查。
顺序表实现后我们也许会在另一个源文件或项目中引用,所以我们这次和三子棋或扫雷一样,分三个部分实现,顺序表的头文件,源文件以及用于测试的源文件。
我们先新建项目,并新建:SeqList.h, SeqList.c, test.c;
我们把引用的头文件和宏定义全部放入SeqList.h中。
我们首先定义结构体,来存顺序表的当前长度、最大长度与顺序表的元素。
//SeqList.h的内容
#include
#include
#include
//我们使用类型重定义 这样在以后更改顺序表数据类型时更加简便
typedef int SLDataType;
typedef struct SeqList{
SLDataType* n;//指向一个动态数组
int size;//当前长度
int capacity;//最大长度
}SL;
接下来就是对顺序表的各种函数的实现与声明:
我们让用户传入顺序表结构体类型的地址,然后把指向动态数组的指针初始化为NULL,并且把长度初始化为0。
//顺序表初始化
void SeqListInit(SL* ps)
{
ps->n = NULL;
ps->size = ps->capacity = 0;
}
既然初始化了,我们就可以开始往里面插入元素了,但是现在顺序表的长度为0,我们必须要对顺序表进行增容,并且我们想一想,增容一次迟早会插满,到时候会继续增容,我们就可以把增容的代码打包成一个函数来实现。
//顺序表的增容
void SeqListCheckCapacity(SL* ps)
{
if(capacity == size) //表示没有空间插入了
{
//当容量为0时初始化给4个容量,其他情况给两倍的容量
int newcapacity = ps->capacity == 0? 4 : ps->capacity*2;
SLDataType* tmp = (SLDataType*)realloc(ps->n, sizeof(SLDataType) * newcapacity);
//用realloc指令动态申请/调整增容
if(tmp == NULL)
{
//增容失败
printf("realloc fail\n");
exit(-1);
}
//增容成功 将指向顺序表的指针返回给ps->n 并把最大容量改成最新的最大容量
ps->n = tmp;
ps->capacity = newcapacity;
}
}
接下来就是顺序表的头插和尾插操作,我们如何插入顺序表呢?
如果说我们要插入一个数在头部,我们需要把1-5依次往后移动,把头部露出一个空间之后,再把值放进去。
所以代码如下:
//顺序表的头插
void SeqListPushFront(SL* ps, SLDataType x)
{
//检查空间是否装满
SeqListCheckCapacity(ps);
//开始头插
int end = ps->size - 1;
while (end >= 0)
{
ps->n[end + 1] = ps->n[end];
end--;
}
ps->n[0] = x;
ps->size++;
}
随后就是顺序表的尾插,既然是尾插,我们就不需要移动了,我们只需要把顺序表当前长度的后一位放入要插入的元素,随后让size++即可。
//顺序表的尾插
void SeqListPushBack(SL* ps, SLDataType x)
{
SeqListCheckCapacity(ps);
ps->n[ps->size] = x;
ps->size++;
}
我们做到了头插元素和尾插元素,现在就要考虑如何去删除头部和尾部的元素了,尾部元素的删除倒是非常简单,只需要让顺序表的size-1就访问不到它了,至于头部该如何删除呢?
我们只需要把后面的元素依次移动过来覆盖,就可以把头部的元素删除了,然后再让size--即可。
//顺序表的头删
void SeqListPopFront(SL* ps)
{
assert(ps && ps->size > 0);
int start = 1;
while (start < ps->size)
{
ps->n[start - 1] = ps->n[start];
start++;
}
ps->size--;
}
//顺序表的尾删
void SeqListPopBack(SL* ps)
{
assert(ps && ps->size > 0);
ps->size--;
}
接下来就是在顺序表的任意位置进行插入和删除,我们需要用户提供需要插入或删除的位置。
在任意位置进行插入时,我们只需要保证插入位置之后的元素都依次后移,腾出位置,与头插的道理类似。但是我们还要主要插入位置的输入是否合法,所以代码如下:
//顺序表的插入
void SeqListInsert(SL* ps, SLDataType x, int pos)
{
if (pos < 0 || pos > ps->size)
{
printf("Pos invalid;\n");
return;
}
SeqListCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos)
{
ps->n[end + 1] = ps->n[end];
end--;
}
ps->n[pos] = x;
ps->size++;
}
至于删除,我们与头删也差不多,只需要删除位置之后的元素依次往前覆盖过来就行了。
//顺序表的删除
void SeqListErase(SL* ps, int pos)
{
assert(ps->size > 0 && pos < ps->size&& ps);
SeqListCheckCapacity(ps);
while (pos < ps->size - 1)
{
ps->n[pos] = ps->n[pos + 1];
pos++;
}
ps->size--;
}
对于查找,我们就从顺序表的表尾(表头)开始查找到表头(表尾),如果有需要查找的元素就返回下标,没有就返回-1。
//顺序表的查找
int SeqListFind(SL* ps, SLDataType x)
{
assert(ps);
int low = ps->size;
while (low--)
{
if (ps->n[low] == x)
return low;
}
return -1;
}
至于打印就非常简单了,利用for循环,和打印数组的方式一致。
//顺序表的打印
void SeqListPrint(SL* ps)
{
system("cls");
int i;
for (i = 0; i < ps->size; i++)
{
printf("%d ", ps->n[i]);
}
printf("\n");
}
#define _CRT_SECURE_NO_WARNINGS 1
#pragma once
//---------以上可忽略----------------------------------------
#include
#include
#include
typedef int SLDataType;
typedef struct SeqList {
SLDataType* n;//顺序表的数据表头
int size;//表示顺序表当前有效数据
int capacity;//表示顺序表的最大容量
}SL;
//初始化顺序表
void SeqListInit(SL* ps);
//检查顺序表容量
void SeqListCheckCapacity(SL* ps);
//顺序表的头尾插入与删除
void SeqListPopFront(SL* ps);
void SeqListPopBack(SL* ps);
void SeqListPushFront(SL* ps, SLDataType x);
void SeqListPushBack(SL* ps, SLDataType x);
//顺序表的打印
void SeqListPrint(SL* ps);
//顺序表的插入与删除与查找
void SeqListInsert(SL* ps, SLDataType x, int pos);
void SeqListErase(SL* ps, int pos);
int SeqListFind(SL* ps, SLDataType x);