顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。
顺序表的底层结构是数组,对数组的封装,实现了常⽤的增删改查等接⼝,并且顺序表的存储一定是连续的,只能从头开始连续存储。
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include
#include
#include
#include
#define SLDataType int
typedef struct SeqList
{
SLDataType* arr;
int size;
int capacity;
}SL;
void SLInit(SL* ps);
void SLDestroy(SL* ps);
void SLPushBack(SL* ps,SLDataType x);
void SLPushFront(SL* ps, SLDataType x);
void SLPopFront(SL* ps);
void SLPopBack(SL* ps);
void SLErase(SL* ps, int pos);
void SLInsert(SL* ps, int pos, SLDataType x);
bool SLFind(SL* ps, SLDataType x);
int SLFindPos(SL* ps, SLDataType x);
void SLRevise(SL* ps, int pos, SLDataType x);
void SLPrint(SL* ps);
bool SLIsEmpty(SL* ps);
typedef struct SeqList
{
SLDataType* arr;
int size;
int capacity;
}SL;
SLDataType* arr;
用来存放顺列表的主体数据,SLDataType
是我们利用define
宏定义所定义的变量,为以后更好的利用顺序表做准备;
size
表示有效数据空间个数;
capacity
表示总的数据空间个数;
好,下面让我们一 一实现下面头文件中的函数。
void SLInit(SL* ps)
{
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* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
assert(tmp);
ps->arr = tmp;
tmp = NULL;
ps->capacity = newcapacity;
}
}
如果满足ps->capacity == ps->size
那么说明空间不足,因为有效数据个数等于总的ps->arr空间存放数据个数,所以这个时候就需要为ps->arr开辟扩展新的空间,一般情况下,空间的扩展倍数是1.5倍或者2倍,这里以2倍为例。
int newcapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;
如果ps->capacity的值为0,我们就不能利用倍数去扩展,而是为他直接开辟空间,所以ps->capacity == 0
成立时,放回值为4,否则返回值为ps->capacity
。
SLDataType* tmp = (SLDataType*)realloc(ps->arr, newcapacity * sizeof(SLDataType));
assert(tmp);
ps->arr = tmp;
新建一个SLDataType*
的tmp变量防止realloc
开辟空间失败,返回空指针而导致ps->arr被置为NULL。
ps->capacity = newcapacity;
最后将新ps->arr
的空间大小赋值给ps->capacity
。
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);
int i = 0;
for (i = ps->size; i > 0; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->size++;
ps->arr[0] = x;
}
这里先判断空间大小,再将所有数据再数组中向后移动一位,再将所要添加的值x
放到数组的首元素地址处,此时ps->size
要记得加一。
void SLPrint(SL* ps)
{
int i = 0;
for (i = 0; i < ps->size; i++)
{
printf("%d ",ps->arr[i]);
}
printf("\n");
}
游历数组arr
逐个打印。
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
SLCheckCapacity(ps);
if (pos < 0 || pos > ps->size)
{
printf("输入错误!!!");
return;
}
for (int i = ps->size; i > pos; i--)
{
ps->arr[i] = ps->arr[i - 1];
}
ps->arr[pos] = x;
ps->size++;
}
先判断pos值是否输入正确,与顺序表空间是否足够。
这里我们只需要将要插入的数组坐标pos之后的数据向后移动一位,再将要插入的数据x
放到arr[pos]
即可。
void SLDestroy(SL* ps)
{
if (ps->arr)
free(ps->arr);
ps->arr = NULL;
ps->capacity = ps->size = 0;
}
if (ps->arr) free(ps->arr);
将顺序表内arr所申请空间释放,并将ps->size,ps->capacity
置为0.
#include"SeqList.h"
int main()
{
SL s;
SLInit(&s);
SLPushBack(&s, 1);
SLPushBack(&s, 2);//1 2
SLPushFront(&s, 0);
SLPushFront(&s, -1);//-1 0 1 2
SLInsert(&s, 0, -2);//-2 -1 0 1 2
SLInsert(&s, s.size, 3);//-2 -1 0 1 2 3
SLInsert(&s, 3, 0);//-2 -1 0 0 1 2 3
SLPrint(&s);
SLDestroy(&s);
return 0;
}
先尾插1,2
再头插0,-1
此时顺序表为-1 0 1 2
,再将-2插入到arr[0]此时顺序表为-2 -1 0 1 2
,再将3插入到arr[5]此时顺序表为-2 -1 0 1 2 3
,最后将0插入到arr[3]此时顺序表为-2 -1 0 0 1 2 3
,程序运行结果如下:
bool SLIsEmpty(SL* ps)
{
assert(ps);
return (ps->size == 0);
}
若有效值ps->size
为0,则返回1.
void SLPopFront(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));
int i = 0;
for (i = 0; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
我们在处理头删数据的时候,assert(!SLIsEmpty(ps))
先判断是否数据存在,若存在,可以让后面的数据对前面进行覆盖,再让ps->size
减一即可,覆盖示意图如下:
void SLPopBack(SL* ps)
{
assert(ps);
assert(!SLIsEmpty(ps));
ps->size--;
}
我们尾删数据只需判断数据是否可删,若可删,我们也只要将ps->size--
即可,因为我们在访问的时候,数组arr从0开始向后访问,而我们利用ps->size
限制了arr
数组从0访问的距离。
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(!SLIsEmpty(ps));
for (int i = pos; i < ps->size - 1; i++)
{
ps->arr[i] = ps->arr[i + 1];
}
ps->size--;
}
这里我们只需要在满足删除条件的时候,找到pos的位置并将后面的数据向前推移即可,再将ps->size - - 。
#include"SeqList.h"
int main()
{
SL s;
SLInit(&s);
for (int i = 0; i < 8; i++)
{
SLPushBack(&s, i);//0 1 2 3 4 5 6 7
}
SLPopBack(&s);
SLPrint(&s);//0 1 2 3 4 5 6
SLPopFront(&s);
SLPrint(&s);//1 2 3 4 5 6
SLErase(&s, 0);
SLPrint(&s);//2 3 4 5 6
SLErase(&s, s.size - 1);
SLPrint(&s);//2 3 4 5
SLErase(&s, 1);
SLPrint(&s);//2 4 5
SLDestroy(&s);
return 0;
}
先利用SLPushBack(&s, i)
循环十次来添加顺序表数据,在进行测试以上代码运行结果如下:
bool SLFind(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if(ps->arr[i]==x)
return true;
}
return false;
}
遍历顺序表中的整个数组,若存在则返回true
,否则返回false
。
int SLFindPos(SL* ps, SLDataType x)
{
assert(ps);
for (int i = 0; i < ps->size; i++)
{
if(ps->arr[i]==x)
return i;
}
return -1;
}
遍历顺序表中的整个数组,当顺序表中的数组元素与查找值相同时,返回数组此时的下标i
,若不存在则返回-1
。
void SLRevise(SL* ps, int pos, SLDataType x)
{
assert(ps);
if (pos >= ps->size && ps < 0)
{
printf("输入错误,无法修改\n");
return;
}
ps->arr[pos] = x;
}
先判断pos是否合理,若合理,则通过pos
直接进行修改。
#include"SeqList.h"
int main()
{
SL s;
SLInit(&s);
for (int i = 0; i < 8; i++)
{
SLPushBack(&s, i);//0 1 2 3 4 5 6 7
}
printf("%d\n", SLFind(&s,0));//1
printf("%d\n", SLFind(&s,7));//1
printf("%d\n", SLFind(&s,8));//0
printf("%d\n", SLFindPos(&s, 0));//0
printf("%d\n", SLFindPos(&s, 3));//3
printf("%d\n", SLFindPos(&s, 7));//7
printf("%d\n", SLFindPos(&s, 8));//-1
printf("修改前:");
SLPrint(&s);
SLRevise(&s, SLFindPos(&s, 3), -3);//将3改为-3
SLRevise(&s, SLFindPos(&s, 7), -7);//将7改为-7
SLRevise(&s, SLFindPos(&s, 0), 10);//将0改为10
printf("修改后:");
SLPrint(&s);
return 0;
}
本期是顺序表的基本功能实现,下期小编将为大家带来基于本期的顺序表所实现的可存储通讯录程序的实现。
那么,喜欢请多多关照把!!!