顺序表是用一段物理地址连续的存储单元存储数据元素的线性结构,一般采用数组存储。在数组上完成数据的增删查改(要求数据连续存储)。
顺序表可以采用的两种方式,一种是静态结构,另一中动态结构(动态结构更优)。
//静态顺序表
#define M 100
typedef int SLDateType;
typedef struct SeqList
{
SLDateType data[N]; //定长数组
int size; //有效数据长度
}SeqList;
2.2 动态顺序表
//动态顺序表
typedef int SLDateType;
typedef struct SeqList
{
SLDateType* data;//用数组存放数据
int size;//实际大小
int capacity;//空间大小
}SeqList;
void SeqListInit(SeqList* ps); //初始化顺序表
void SeqListDestroy(SeqList* ps); //销毁顺序表
void SeqListPrint(SeqList* ps); //打印顺序表
void SeqListPushBack(SeqList* ps, SLDateType x); //尾插
void SeqListPushFront(SeqList* ps, SLDateType x); //头插
void SeqListPopFront(SeqList* ps); //头删
void SeqListPopBack(SeqList* ps); //尾删
// 顺序表查找
int SeqListFind(SeqList* ps, SLDateType x);
// 顺序表在pos位置插入x
void SeqListInsert(SeqList* ps, int pos, SLDateType x);
// 顺序表删除pos位置的值
void SeqListErase(SeqList* ps, int pos);
首先得进行初始化,初始化时可以先给一定空间,空间满了会进行扩容,这里我们起初给的容量的是4。
//初始化顺序表
void SeqListInit(SeqList* ps)
{
ps->a = (SLDateType*)malloc(sizeof(SLDateType) * 4);
if (ps->a==NULL)
{
perror("malloc");
exit(-1);
}
ps->size = 0;
ps->capacity = 4;
}
每当顺序表容量满时(4个满),可以使用函数realloc来对顺序表进行扩容,每次增加4,这样动态开辟空间,就会减少空间的浪费,和避免空间不足问题。
//顺序表的扩容
void SLCheckCapacity(SeqList* ps)
{
if (ps->size == ps->capacity)
{
SLDateType* tem = (SLDateType*)realloc(ps->a, ps->capacity * 2 * (sizeof(SLDateType)));
if (tem == NULL)
{
perror("realloc");
exit(-1);
}
ps->a = tem;
ps->capacity *= 2;
}
}
将顺序表节点的内容打印出来。
//打印顺序表
void SeqListPrint(SeqList* ps)
{
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
下面介绍三种顺序表的插入,让我们更好的理解顺序表的链接。
在顺序表的表头插入一个节点,将顺序表的所有节点从后往前依次后移,再将新节点插入到第一个节点位置。
void SeqListPushFront(SeqList* ps, SLDateType x) //从后往前移动
{
SLCheckCapacity(ps);
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;
}
尾插法需要先找到顺序表的尾部,然后再将新节点插入到尾部。
void SeqListPushBack(SeqList* ps, SLDateType x)
{
SLCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
}
在指定位置插入元素,先找到pos的前一个结点,用来标记,然后插入pos,后面元素从后往前依次后移。
void SeqListInsert(SeqList* ps, int pos, SLDateType x) //从后往前移动
{
SLCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos-1)
{
ps->a[end + 1] = ps->a[end];
end--;
}
ps->a[pos-1] = x;
ps->size++;
}
顺序表的插入和删除是对应的,既然如此,我们对应插入写三个相对应的删除就比较的简单了。
删除第一个节点,并且将后面节点从前往后依次前移一位。
void SeqListPopFront(SeqList* ps) //从前往后
{
assert(ps->size);
for (int i = 1; i < ps->size; i++)
{
ps->a[i - 1] = ps->a[i];
}
ps->size--;
}
尾删很简单的一种就是,直接将长度减一,就可以达到尾删的目的。
void SeqListPopBack(SeqList* ps)
{
assert(ps->size);
ps->size--;
}
删除指定位置的元素,先找到pos位置,再将前一个元素依次从前往后覆盖上一个元素,达到删除的目的。
void SeqListErase(SeqList* ps, int pos)
{
assert(ps->size);
for (int i = pos-1; i < ps->size; i++)
{
ps->a[i] = ps ->a[i + 1];
}
ps->size--;
}
查找元素,遍历一遍顺序表(数组),找到返回i+1(物理位置),没找到返回-1.
//在顺序表中查找
int SeqListFind(SeqList* ps, SLDateType x)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i+1;
}
}
return -1;
}
3.7 销毁顺序表
将顺序表空间释放,并且将顺序表指针制空,防止内存泄漏(好习惯)。
void SeqListDestroy(SeqList* ps)
{
free(ps->a);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
到这里顺序表的基本问题就解释完了,相信多多少少会解决大家心头的疑问,在数据结构的学习中应当善于思考,多画图,死磕代码,注意细节,将伪代码转换为代码,这样才能很好的掌握数据结构的有关知识,共勉,加油!!!