顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删差改。
顺序表数据必须挨着存,储存方式如下:(本质上就是数组)
顺序表一般分为静态(定长数组存储)和动态顺序表(动态开辟的数组存储),常用动态。
那么接下来可以写动态顺序表的接口函数了
这里接口函数的实现是根据C++中的STL库函数中的功能来模拟实现:
先定义头文件来包含具体的接口函数功能:
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
// 要求:存储的数据从0开始,依次连续存储
// 静态的顺序表
// 问题:开小了,不够用。开大了,存在浪费。
//#define N 10000
//struct SeqList
//{
// int a[N]; //这里就是静态,直接定义好数据个数,跟动态有所区别
// int size; // 记录存储了多少个数据
//};
// 动态的顺序表
typedef struct SeqList
{
SLDataType* a; //这里是动态顺序表,所以可以用一个指针来指向动态开辟的空间,
int size; // 存储数据个数
int capacity; // 存储空间大小
}SL, SeqList;
void SeqListPrint(SeqList* ps);
//void SLInit(SeqList* ps);
void SeqListInit(SeqList* ps);
void SeqListDestroy(SeqList* ps);
void SeqListCheckCapacity(SeqList* ps);
// 时间复杂度是O(1)
void SeqListPushBack(SeqList* ps, SLDataType x);
void SeqListPopBack(SeqList* psl);
// 时间复杂度是O(N)
void SeqListPushFront(SeqList* ps, SLDataType x);
void SeqListPopFront(SeqList* ps);
// 在pos位置插入x
void SeqListInsert(SeqList* ps, size_t pos, SLDataType x);
// 删除pos位置的数据
void SeqListErase(SeqList* ps, size_t pos);
// 顺序表查找
int SeqListFind(SeqList* ps, SLDataType x);
接下来就需要我们去一个个实现:
先实现简单的:
打印函数 :跟打印数组一个意思
void SeqListPrint(SL* ps)
{
assert(ps);
for (int i = 0; i < ps->size; ++i)
{
printf("%d ", ps->a[i]);
}
printf("\n");
}
初始化函数:将结构体成员置0;指针置空
void SeqListInit(SL* ps)
{
assert(ps);
ps->a = NULL;
ps->size = ps->capacity = 0;
}
销毁函数:将指针开辟的空间free掉,将结构体成员置0;指针置空。
void SeqListDestory(SL* ps)
{
assert(ps);
free(ps->a);
ps->a = NULL;
ps->capacity = ps->size = 0;
}
核查容量:这里的逻辑是判断当前容量是否等于最大容量,若相等,则扩容。
void SeqListCheckCapacity(SL* ps)
{
assert(ps);
if (ps->size == ps->capacity)
{
int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDataType* tmp = (SLDataType*)realloc(ps->a, newcapacity*sizeof(SLDataType));
if (tmp == NULL)
{
printf("realloc fail\n");
exit(-1);
}
ps->a = tmp;
ps->capacity = newcapacity;
}
}
接下里这四个函数都是两种写法
尾插函数:这个也比较简单,先核查容量,再加入数据x,然后size++
void SeqListPushBack(SeqList* ps, SLDataType x)
{
assert(ps);
SeqListCheckCapacity(ps);
ps->a[ps->size] = x;
ps->size++;
//SeqListInsert(ps, ps->size, x);
}
尾删函数:这个也简单,温柔和暴力的原理都一样,
void SeqListPopBack(SL* ps)
{
// 温柔处理方式
//if (ps->size > 0)
//{
// //ps->a[ps->size - 1] = 0;
// ps->size--;
//}
// 暴力处理方式
assert(ps->size > 0);
ps->size--;
//写了指定位置删除后的方式
SeqListErase(ps, ps->size-1);
}
这两个比较麻烦,因为都需要挪动数据。
头插函数:
void SeqListPushFront(SL* ps, SLDataType x)
{
//SeqListCheckCapacity(ps);
挪动数据
//int end = ps->size - 1;
//while (end >= 0)
//{
// ps->a[end + 1] = ps->a[end];
// --end;
//}
//ps->a[0] = x;
//ps->size++;
SeqListInsert(ps, 0, x);
}
头删函数:
void SeqListPopFront(SL* ps)
{
assert(ps->size > 0);
// 挪动数据
//int begin = 0;
//while (begin < ps->size-1)
//{
// ps->a[begin] = ps->a[begin+1];
// ++ begin;
//}
//ps->size--;
//删除函数的写法
SeqListErase(ps, 0);
}
指定pos下表位置插入:和头插函数几乎一样
void SeqListInsert(SL* ps, int pos, SLDataType x)
{
// 温柔的处理方式//
/*if (pos > ps->size || pos < 0)
{
printf("pos invalid\n");
return;
}*/
assert(pos >= 0 && pos <= ps->size);
SeqListCheckCapacity(ps);
// 挪动数据
int end = ps->size - 1;
while (end >= pos)
{
ps->a[end + 1] = ps->a[end];
--end;
}
ps->a[pos] = x;
ps->size++;
}
删除pos位置的函数:
void SeqListErase(SL* ps, int pos)
{
assert(pos >= 0 && pos < ps->size);
int begin = pos + 1;
while (begin < ps->size)
{
ps->a[begin - 1] = ps->a[begin];
++begin;
}
ps->size--;
}
最后一个 查找函数:
int SeqListFind(SL* ps, SLDataType x)
{
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
return i;
}
}
return -1;
}
其实顺序表的实现还是挺简单的,本质上就是动态的数组存在结构体中,然后用指针去维护。