数据结构-顺序表SeqList详解(增删改查)
1.线性表
- 线性表是最基本、最简单、也是最常用的一种数据结构。线性表*(linear list)*是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列。
- 常见的线性表:顺序表,链表,栈,队列,字符串…
- 线性表在逻辑上是线性结构,也就是一个元素挨着一个元素,但是物理层面不一定是连续的,线性表在物理上存储通常以数组和链式结构存储
顺序表
链表
2.顺序表
1)顺序表的定义
顺序表是在计算机内存中以数组的形式保存的[线性表],线性表的顺序存储是指用一组地址连续的存储单元依次存储线性表中的各个元素、使得线性表中在逻辑结构上相邻的数据元素存储在相邻的物理存储单元中。
2)顺序表的创建
typedef int SLDataType //类型重命名 使线性表存储多种元素
typedef struct SeqList{
SLDataType* a;//存储元素的数组
size_t size;// 顺序表中存储的元素个数
int capacity;//顺序表的最大存储个数
}SeqList
3)顺序表需要的接口及实现
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);
3.1顺序表的初始化
void SeqListInit(SeqList* ps) {
ps->capacity = 0;
ps->a = NULL;
ps->size = 0;
}
为了防止传入指针为NULL,对ps指针进行断言
void SeqListInit(SeqList* ps) {
assert(ps);
ps->capacity = 0;
ps->a = NULL;
ps->size = 0;
}
3.2顺序表的销毁
void SeqListDestroy(SeqList* ps) {
if (ps->a != NULL)
{
ps->capacity = 0;
ps->size = 0;
free(ps->a);
ps->a = NULL;
}
}
销毁顺序表,只需要销毁顺序表结构体变量中的数组成员变量,因为这个数组是我们手动申请的,然后在把容量和元素个数置0即可。
**误区!!!**为什么不直接销毁ps指针指向的空间? 因为ps指针指向的那块空间不是我们手动申请的,所以没有权限释放
3.3顺序表的尾插
插入前检查容量是否足够,足够则继续添加
如果容量不够则需要进行扩容,将代表容量的变量扩大,如果是第一次扩容则赋初值为4,使用realloc函数将原指针传入,用一个临时变量来接收返回值,
因为有可能扩容失败返回的值为空,如果返回的值不为空说明扩容成功,将新的容量和指针赋值回去。
可以将扩容逻辑封装成一个函数。因为扩容需要在多种函数中被调用。
void check(SeqList* ps) {
assert(ps);
if (ps->capacity == ps->size)
{
size_t new_capacity = ps->capacity == 0 ? 4 : ps->capacity * 2;
SLDateType* p = realloc(ps->a, new_capacity * sizeof(SLDateType));
if (p == NULL)
{
perror("realloc fail");
return;
}
ps->a = p;
ps->capacity = new_capacity;
}
}
void SeqListPushBack(SeqList* ps, SLDateType x){
//插入前检查容量是否足够,足够则继续添加
//如果不够则需要进行扩容,将代表容量的变量扩大,如果是第一次扩容则赋初值为4,使用realloc函数将原指针传入,用一个临时变量来接收返回值,
//因为有可能扩容失败返回的值为空,如果返回的值不为空说明扩容成功,将新的容量和指针赋值回去。
assert(ps);
check(ps);
ps->a = tmp;
ps->capacity = new_capacity;
}
ps->a[ps->size++] = x;
}
3.4顺序表的输出
void SeqListPrint(SeqList* ps) {
printf("顺序表长度为%d 剩余容量为%d\n", ps->size, ps->capacity);
for (int i = 0; i < ps->size; i++)
{
printf("%d ", ps->a[i]);
}
}
test 1-4
void test1_4() {
SeqList ps;
SeqListInit(&ps);
SeqListPushBack(&ps, 1);
SeqListPushBack(&ps, 2);
SeqListPushBack(&ps, 3);
SeqListPushBack(&ps, 4);
SeqListPushBack(&ps, 5);
SeqListPushBack(&ps, 6);
SeqListPushBack(&ps, 7);
SeqListPushBack(&ps, 8);
SeqListPushBack(&ps, 9);
SeqListPrint(&ps);
SeqListDestroy(&ps);
}
3.5顺序表的头插
头插法的时间复杂度是O(n),每个元素都要向后移动一次。在移动前要检查数组是否还有剩余的位置,如果没有剩余位置需要进行扩容,
如果有剩余位置,直接移动然后将数组的第一个元素赋值,然后将顺序表的size自增。
void SeqListPushFront(SeqList* ps, SLDateType x) {
assert(ps);
check(ps);
for (int i = ps->size-1; i >= 0; i--)
{
ps->a[i + 1] = ps->a[i];
}
ps->a[0] = x;
ps->size++;
}
3.6顺序表的头删
如果顺序表的元素个数为0,那就代表顺序表没有元素,无法继续删除。如果元素个数不为0,那就把第n个位置的元素移动到n-1上,n小于size
void SeqListPopFront(SeqList* ps) {
assert(ps);
assert(ps->size);
for (size_t i = 1; i < ps->size ; i++)
{
ps->a[i-1] = ps->a[i];
}
ps->size--;
}
3.7顺序表的尾删除
void SeqListPopBack(SeqList* ps) {
assert(ps);
ass
ps->size--;
}
3.8顺序表的查找
int SeqListFind(SeqList* ps, SLDateType x) {
assert(ps);
int index = -1;
for (int i = 0; i < ps->size; i++)
{
if (ps->a[i] == x)
{
index = i;
break;
}
}
return index;
}
test5-8
void test5_8() {
SeqList s;
SeqListInit(&s);
SeqListPushFront(&s, 5);
SeqListPushFront(&s, 4);
SeqListPushFront(&s, 3);
SeqListPushFront(&s, 2);
SeqListPushFront(&s, 1);
SeqListPrint(&s);
SeqListPopFront(&s);
SeqListPrint(&s);
SeqListPopFront(&s);
SeqListPrint(&s);
SeqListPopBack(&s);
SeqListPrint(&s);
int x = SeqListFind(&s,3);
printf("%d \n", x);
}
3.9顺序表在pos位置插入x
插入元素前先断言,并检查容量,没有问题就将pos起始包括pos位置的元素向后移动一个单位,然后插入
void SeqListInsert(SeqList* ps, int pos, SLDateType x) {
assert(ps);
assert(pos >= 0 && pos <= ps->size);
check(ps);
for (int i = ps->size-1; i >= pos; i--)
{
ps->a[i + 1] = ps->a[i];
}
ps->a[pos] = x;
ps->size++;
}
测试
void test_SeqListInsert() {
SeqList s;
SeqListInit(&s);
//SeqListInsert(&s,1,9);
//SeqListInsert(&s, -1, 9);
SeqListPushFront(&s, 5);
SeqListPushFront(&s, 4);
SeqListPushFront(&s, 3);
SeqListPushFront(&s, 2);
SeqListPrint(&s);
SeqListPopFront(&s);
SeqListPrint(&s);
SeqListInsert(&s,0,0);
SeqListInsert(&s, 3, 7);
SeqListInsert(&s, 5, 10);
SeqListPrint(&s);
}
3.10 顺序表删除pos位置的值
删除元素前断言pos和传入的指针变量,注意指针在前,然后从pos位置开始元素依次向前挪动
void SeqListErase(SeqList* ps, int pos) {
assert(ps);
assert(pos >= 0 && pos < ps->size);
for (int i = pos; i < ps->size-1; i++)
{
ps->a[i] = ps->a[i+1];
}
ps->size--;
}
测试
void testSeqListErase() {
SeqList s;
SeqListInit(&s);
SeqListPushBack(&s, 0);
SeqListPushBack(&s, 1);
SeqListPushBack(&s, 2);
SeqListPushBack(&s, 3);
SeqListPushBack(&s, 4);
SeqListPrint(&s);
SeqListErase(&s, 2);
SeqListPrint(&s);
SeqListErase(&s, 2);
SeqListPrint(&s);
SeqListErase(&s, 1);
SeqListPrint(&s);
SeqListErase(&s, 1);
SeqListPrint(&s);
SeqListErase(&s, 0);
SeqListPrint(&s);
}