目录
前言
本篇进入顺序表的讲解,将主要介绍顺序表的概念及结构和实现动态顺序表。
顺序表概念及结构
一.概念
顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存 储。在数组上完成数据的增删查改。
二.结构
1. 静态顺序表:使用定长数组存储元素。
#define N 10000
struct SeqList
{
int a[N];
int size;
};
看上面这串代码,即为静态顺序表,它有个很大的缺点,即N的值开少了不够用,开多了浪费,我们一般不用静态顺序表,而用下面的动态顺序表表示。
2. 动态顺序表:使用动态开辟的数组存储。
typedef struct SeqList
{
SLDataType* a;
int size;
int capacity;
}SL;
上面的代码,a表示指向动态开辟的数组,size表示有效数据个数,capacity表示容量空间的大小,这个就比上面那静态顺序表的代码要好,当他内存不够时可以通过增容来开辟内存,所以显示中基本使用动态顺序表,根据需要动态分配空间。
动态顺序表的实现
一.顺序表的初始化
void SLInit(SL* ps)
{
assert(ps);
ps->a = (SLDataType*)malloc(sizeof(SLDataType)* INIT_CAPACITY);
if (ps->a == NULL)
{
perror("malloc fail");
return;
}
ps->size = 0;
ps->capacity = INIT_CAPACITY;
}
SLInit表示顺序表的初始化,assert表示断言,判断指针是否为空,malloc函数为ps申请一段空间,ps->size=0时把顺序表的当前长度设为0,ps->capacity = INIT_CAPACITY为 顺序表的最大长度,到这就完成了初始化了。
二.顺序表空间的检查及扩容
void SLCheckCapacity(SL* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
SLDataType* tmp = (SLDataType*)realloc(ps->a, sizeof(SLDataType) * ps->capacity * 2);
if (tmp == NULL)
{
perror("realloc fail");
return;
}
ps->a = tmp;
ps->capacity *= 2;
}
}
顺序表要扩容有两种情况,第一种为顺序表为空及tmp == NULL,第二种及为达到最大限度时,都要进行扩容,一般是扩容两倍,及ps->capacity *= 2。
三.顺序表在pos位置上插入x
void SLInsert(SL* ps, int pos, SLDataType x)
{
assert(ps);
assert(pos >= 0 && pos <= ps->size);
SLCheckCapacity(ps);
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
在插入过程中,首先要断言,判断ps和pos;其次通过SLCheckCapacity函数对ps进行检验,判断是否要扩容;end表示ps中有效数据的个数减一,而a[end]则表示a的最后一个数据,此while循环表示从pos后面的数向后退一位,其是从后向前进行循环;将x插入最后将x插入a中。
四.顺序表删除pos
void SLErase(SL* ps, int pos)
{
assert(ps);
assert(pos >= 0 && pos < ps->size);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
}
通过assert来判断ps和pos是否为空,begin表示pos后一个数据,while循还表示将pos位置的数据删掉,后面的所有数据向前移一位。
五.顺序表尾插
void SLPushBack(SL* ps, SLDataType x)
{
//assert(ps);
扩容
//SLCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
//ps->a[ps->size++] = x;
SLInsert(ps, ps->size, x);
}
尾插通过SLInsert就可以轻松实现顺序表尾插。
五.顺序表尾删
void SLPopBack(SL* ps)
{
//assert(ps);
暴力检查
//assert(ps->size > 0);
温柔的检查
if (ps->size == 0)
// //return;
ps->a[ps->size - 1] = 0;
//ps->size--;
SLErase(ps, ps->size - 1);
}
尾删通过SLErase可以轻松实现顺序表尾删。
六.顺序表头插
void SLPushFront(SL* ps, SLDataType x)
{
/*assert(ps);
SLCheckCapacity(ps);
int end = ps->size - 1;
while (end >= 0)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[0] = x;
ps->size++;*/
SLInsert(ps, 0, x);
}
同上,头插通过SLInsert就可以轻松实现顺序表头插。
七.顺序表头删
void SLPopFront(SL* ps)
{
//assert(ps);
//assert(ps->size > 0);
//int begin = 1;
//while (begin < ps->size)
//{
// ps->a[begin - 1] = ps->a[begin];
// ++begin;
//}
//ps->size--;
SLErase(ps, 0);
}
头删通过SLErase可以轻松实现顺序表头删。
八.顺序表查找
int SLFind(SL* ps, SLDataType x)
{
assert(ps);
for(int i = 0; i < ps->size; ++i)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
顺序表查找的话先断言,然后逐个遍历数组,最后返回查找到的数值。
九.顺序表的销毁
void SLDestroy(SL* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
通过free释放a这个数组,令a变为空,归还这片空间。
十.顺序表打印
void SLPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; ++i)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
遍历数组,进行打印。
总结
到这里,本章顺序表的内容就完结了,在下一篇博客就将进入链表的讲解。各位佬,看到这里给个三连支持一下吧,我会顺着品论区回访的。