顺序表分为静态和动态,静态的顺序表和动态顺序表相关接口的实现差别不大,因此不在赘述。
#pragma once
#include
#include
#include
//#define N 10
#define INIT_CAPACITY 4 // 给顺序表一个初始大小
typedef int SLDataType; //定义顺序表数据类型
静态顺序表 开少了不够用 开多了浪费
//typedef struct SeqList {
// SLDataType arr[N];
// int size;
//};
//动态顺序表 动态扩容 开辟内存在堆区 没必要用柔性数组
typedef struct SeqList {
SLDataType* base; //动态数组的基地址
int size; //有效数据个数 size是 最后一个元素的下一个位置的下标
int capacity; //空间容量
}SeqList;
此处阐释静态顺序表的劣势。
静态顺序表开辟的空间是固定的,开辟空间过少,不够存储, 开辟空间过多, 造成空间的浪费。因此在实际使用的过程中,使用动态顺序表居多。
使用指针管理顺序表的地址, 利用C语言的malloc函数和realloc函数以及指针来实现顺序表的动态扩容。
关于size的理解:
数据结构的功能无非就是增、删、查、改以及遍历,接口如下:
// 数据结构管理的需求 增删查改
void SLInit(SeqList* ps); //初始化
void SLDestroy(SeqList* ps); //销毁
void checkCapacity(SeqList* ps); //检查容量与扩容 将扩容抽象成一个函数
void SLPushBack(SeqList* ps, SLDataType data); // 尾插与尾删
void SLPopBack(SeqList* ps);
void SLPushFront(SeqList* ps, SLDataType data); // 头插与头删
void SLPushFront(SeqList* ps, SLDataType data);
void SLInsert(SeqList* ps, int pos, SLDataType data); //在指定位置(给定数组下标)插入删除元素
void SLErase(SeqList* ps, int pos);
int FindSL(SeqList* ps, SLDataType ele); //查找 顺序遍历暴力查找法
//打印顺序表
void SLPrint(SeqList* ps);
#include "SeqList.h"
//传入assert的值为真时, 通过 为假 报错, 并在终端中显示错误所在行
void SLInit(SeqList* ps) {
assert(ps);
ps->base = (SLDataType*)malloc(sizeof(SLDataType) * INIT_CAPACITY);
if (ps->base == NULL) {
perror("malloc failed");
return;
}
ps->size = 0;
ps->capacity = INIT_CAPACITY;
}
void SLDestroy(SeqList* ps) {
assert(ps);
free(ps->base);
ps->base = NULL;
ps->capacity = ps->size = 0;
}
一个一个插的时候,会到size == capacity的临界点, 因此只需检查临界点
void checkCapacity(SeqList* ps) {
assert(ps);
//扩容
// 一个一个插的时候,会到size == capacity的临界点, 因此只需检查临界点
if (ps->size == ps->capacity) {
//将容量扩至原来的两倍
SLDataType* newSpace = (SLDataType*)realloc(ps->base, sizeof(SLDataType) * ps->capacity * 2);
if (newSpace == NULL) {
perror("realloc failed");
return;
}
// realloc 函数 扩容会有两种情况 一种是开辟一个新的空间 另一种是在原空间的基础上扩容
ps->base = newSpace;
ps->capacity *= 2;
}
}
// 尾插与尾删
void SLPushBack(SeqList* ps, SLDataType data) {
assert(ps);
checkCapacity(ps);
//插入数据
ps->base[ps->size++] = data;
//可以用任意位置插入代替尾插
//SLInsert(ps, ps->size, data);
}
void SLPopBack(SeqList* ps) {
//size的值为 0 时, 就不能再缩小了
//暴力检查
assert(ps->size > 0);
//温柔的检查
if (ps->size == 0)
return;
ps->size--;
//SLErase(ps, ps->size - 1);
}
插入N个数据的时间复杂度为O(N)
while (end >= 0) {
ps->base[end + 1] = ps->base[end];
end--;
}
void SLPushFront(SeqList* ps, SLDataType data){
assert(ps);
int end = ps->size - 1;
checkCapacity(ps); //检查顺序表是否为满的
while (end >= 0) {
ps->base[end + 1] = ps->base[end];
end--;
}
ps->base[0] = data;
ps->size++;
}
void SLPopFront(SeqList* ps, SLDataType data) {
assert(ps);
assert(ps->size > 0);
int begin = 1;
while (begin < ps->size) {
ps->base[begin - 1] = ps->base[begin];
begin++;
}
ps->size--;
}
void SLInsert(SeqList* ps, int pos, SLDataType data) {
assert(ps);
assert(pos >= 0 && pos <= ps->size);
checkCapacity(ps);
int end = ps->size - 1;
while (end >= pos) {
ps->base[end + 1] = ps->base[end];
end--;
}
ps->base[pos] = data;
ps->size++;
}
void SLErase(SeqList* ps, int pos) {
assert(ps);
assert(pos >= 0 && pos <= ps->size - 1);
int begin = pos + 1;
while (begin <= ps->size) {
ps->base[begin - 1] = ps->base[begin];
begin++;
}
ps->size--;
}
int FindSL(SeqList* ps, SLDataType ele) {
assert(ps);
for (int i = 0; i < ps->size; i++) {
if (ps->base[i] == ele)
return i;
}
return -1;
}
void SLPrint(SeqList* ps) {
assert(ps && ps->base);
for (int i = 0; i < ps->size; ++i) {
printf("%d ", ps->base[i]);
}
printf("\n");
}