1.顺序表的本质
顺序表本质上来说呢,就是数组,只不过它的空间可以动态增长,并且要求它里面的存储数据的方式必是从左往右连续的。它归属于线性表,因为它的逻辑结构是线性的,同时物理结构也是线性的。(这里提醒一点:线性表只是表示逻辑结构线性的,并不要求其物理结构连续,例如链表的物理结构就不是线性的)
线性表又分为静态线性表和动态顺序表:
(1) 静态顺序表:使用定长数组存储。
(2) 动态顺序表:使用动态开辟的数组存储。
那动态线性表那么好,又为啥开辟了那么多新的数据结构呢:答案是动态线性表肯定是有缺陷的。
2.动态线性表的缺陷:1.动态增容有性能损耗2.当需要头部插入一个数据时,需要将后面的数据一一往后挪,这将是一个时间复杂度为o(n)的操作,你可能认为才o(n),但对于积土成山,这终究不是什么好事,肯定会对效率产生一定影响。
3.动态顺序表的创建以及初始化:(1)对于创建首先要说明的是这个a指针是指向数组,为什么用指针,就是为了方便后续开辟更多空间,如果是数组那么空间固定,就是静态顺序表了。然后对于把int类型定义别名为SeqDataType,是为了后续便于改变顺序表中数据类型(就是只要再开头改一下即可)
typedef int SeqDataType;
typedef struct SeqList
{
SeqDataType* a;//指向动态开辟的数组
int size;//有效数据个数
int capacity;//容量
}SeqList;
(2)初始化:这里我们讲一下assert函数,它是断言函数,assert(x)这个x语句放的是一个判断,如果判断出错,程序会停止,并告诉你在哪出错,这样方便于我们来找出错误,而不是从第一行傻傻的看代码。。
void SeqListInit(SeqList* pq)
{
//assert(pq != NULL);这一句与下一句作用相同
assert(pq);
pq->a = NULL;
pq->size = pq->capacity = 0;
}
(3)销毁:当然我们会初始化了,肯定还得会销毁。
这里我们强调一点,就是free函数,只是把指针指向的空间释放,并不是把指针指向空,所以我们之后还得让指针指向空。
void SeqListDestroy(SeqList* pq)
{
assert(pq);
free(pq->a);
pq->a = NULL;
pq->capacity = pq->size = 0;
}
(4)扩容:所谓动态,就是可以随着我们插入数据,改变我们数据结构的空间大小,那必然会牵扯到扩容。
void SeqCheckCapacity(SeqList* pq)
{//满了,需要增容
assert(pq);
if (pq->size == pq->capacity)
{
int newcapacity = pq->capacity * 2;
SeqDataType* newA = realloc(pq->a, sizeof(SeqDataType)*newcapacity);
if (newA == NULL)
{
printf("realloc fail\n");
exit(-1);
}
pq->a = newA;
pq->capacity = newcapacity;
}
}
void SeqCheckCapacity(SeqList* pq)
{//满了,需要增容
assert(pq);
if (pq->size == pq->capacity)
{
int newcapacity = pq->capacity == 0 ? 4 : pq->capacity * 2;
SeqDataType* newA = realloc(pq->a, sizeof(SeqDataType)*newcapacity);
if (newA == NULL)
{
printf("realloc fail\n");
exit(-1);
}
pq->a = newA;
pq->capacity = newcapacity;
}
}
关于以上两个代码,聪明的你们,肯定一眼就看出不同,就是如果初始容量为0,那么0*2还为0,这样不管乘多少次,它都达不到扩容的作用。这也是很多人忽视的一点。
关于realloc函数,我们讲一点:realloc函数会探测后续空间够不够,(1)如果够,那么直接在后面开辟空间(2)如果不够,就会重新找一块大的空间,将原来的数据拷贝,粘贴到新空间,并将原空间释放。
(5)打印顺序表
这个简单,就是将表遍历一遍即可。
void SeqListPrint(SeqList* pq)
{
assert(pq);
for (int i = 0; i < pq->size; i++)
{
printf("%d ", pq->a[i]);
}
printf("\n");
}
(6)顺序表的接口函数:
接口函数有很多,我们先声明一下
void SeqListPushBack(SeqList* pq, SeqDataType x);//尾插
void SeqListPushFront(SeqList* pq, SeqDataType x);//头插
void SeqListPopBack(SeqList* pq);//尾删
void SeqListPopFront(SeqList* pq);//头删
int SeqListFind(SeqList* pq, SeqDataType x);//查找
void SeqListInsert(SeqList* pq,int pos, SeqDataType x);//插入
void SeqListErase(SeqList* pq, int pos);//删除
void SeqListModify(SeqList* pq, int pos, SeqDataType x);//更改数据
看到这么多函数是不是脑子又不够用啦,但细心的小伙伴又发现了,你看这底下删除函数和插入函数都有了,不是可以直接替代上面四个函数吗?我想说确实可以,但学知识是循序渐进的,先从简单的头插尾插,头删尾删开始,掌握规律,再来把它升华。
(1)因为和数组差不多,尾插尾删较为简单:上代码!!!
void SeqListPushBack(SeqList* pq, SeqDataType x)//尾插
{
assert(pq);
SeqCheckCapacity(pq);
pq->a[pq->size] = x;
pq->size++;
}
void SeqListPopBack(SeqList* pq)//尾删
{
assert(pq);
assert(pq->size > 0);
pq->size--;
}
(2)头插头删:这里我们就需要挪动数据,头插是从后到前依次往后挪,头删是从前到后依次往前挪。废话少说上代码!!
void SeqListPushFront(SeqList* pq, SeqDataType x)//头插
{
assert(pq);
SeqCheckCapacity(pq);
int end = pq->size - 1;
while (end >= 0)
{
pq->a[end + 1] = pq->a[end];
end--;
}
pq->a[0] = x;
pq->size++;
}
void SeqListPopFront(SeqList* pq)//头删
{
assert(pq);
assert(pq->size > 0);
int begin = 0;
while (begin<pq->size-1)
{
pq->a[begin] = pq->a[begin + 1];
begin++;
}
pq->size--;
}
(3)查找算法,其实较为简单,说到底就是一次数组遍历:
int SeqListFind(SeqList* pq, SeqDataType x)//查找
{
assert(pq);
for (int i = 0; i < pq->size; i++)
{
if (pq->a[i] == x)
{
return i;
}
}
return -1;
}
(4)插入删除算法:插入删除在学会头插头删挪动方法的基础,就也不难。
void SeqListInsert(SeqList* pq, int pos, SeqDataType x)
{
assert(pq);
assert(pos >= 0 && pos <= pq->size);//小于等于是为了方便尾插
SeqCheckCapacity(pq);
int end = pq->size - 1;
while (end >= pos)
{
pq->a[end + 1] = pq->a[end];
end--;
}
pq->a[pos] = x;
pq->size++;
}
void SeqListErase(SeqList* pq, int pos)//删除
{
assert(pq);
assert(pos >= 0 && pos <= pq->size - 1);
int begin = pos;
while (begin <= pq->size - 1)
{
pq->a[begin] = pq->a[begin + 1];
begin++;
}
pq->size--;
}
(5)更改:与数组操作相似
void SeqListModify(SeqList* pq, int pos, SeqDataType x)
{
assert(pq);
assert(pos >= 0 && pos <= pq->size - 1);
pq->a[pos] = x;
}
到这里关于顺序表的介绍就这么多了,希望对你有所收获。如有收获,请给点赞。