这里是只讲干货不讲废话的炽念,这个系列的文章是为了我自己以后复习数据结构而写,所以可能会用一种我自己能够听懂的方式来描述,不会像书本上那么枯燥和无聊,且全系列的代码均是可运行的代码,关键地方会给出注释^_^
全文近8000字
版本:C++17
编译器:Clion 2023.3.24
暂时只给出代码,不会涉及到基础知识的讲解
1.线性表的定义
//定义顺序表的表头结构
typedef struct {Element* data; // 指向顺序表的数据区域int len; // 该区域能够访问的边界条件,目前内容器存储数据的数量int cap; // 该区域最大容量,超过这个容量就需要扩容
}SeqList;// len 始终指向待插入位置
2.线性表的实现函数
2.1.表的创建以及删除
// 表的创建以及删除
SeqList *createSeqList(int n); // 创造一个表并初始化void releaseSeqList(SeqList *seqList); // 释放掉表头以及表内的数据
2.2.扩容函数
// 辅助函数
int enLargerSeq(SeqList *seqList); // 扩容函数
2.3.向表中插入元素
// 三种插入方式
int pushBackSeq(SeqList *seqList, Element val); // 往尾部插入一个元素int headInsertSeq(SeqList *seqList, Element val); // 往头部插入一个元素int insertSeq(SeqList *seqList, int pos, Element val); // 按指定值来插入元素
2.4.表的展示以及查找
// 展示以及查找
void showSeqList(SeqList *seqList); // 遍历并显示数据int findSeq(SeqList *seqList, Element val); // 按值查找
2.5.删除表中的元素
// 三种方式删除
int deleteSpeSeq(SeqList *seqList, Element val); // 删除特定值的元素int deleteHeadSeq(SeqList *seqList); // 删除首元素int deleteTailSeq(SeqList *seqList); // 删除尾元素
3.实现函数的代码
3.1.createSeqList函数的实现
函数定义:
SeqList *createSeqList(int n); // 创造一个表并初始化
函数功能:
该函数用于创建一个长度为n的顺序表,并返回指向该顺序表的指针
实现思路:
a.申请表头空间b.申请表内元素的空间c.初始化 len(length 长度) 和 cap(capacity 容量)d.对申请的空间进行检查,如果申请空间失败,则需要返回NULL
具体实现代码:
SeqList *createSeqList(int n) {SeqList *seqList;seqList = (SeqList *)malloc(sizeof(SeqList)); //申请表头if (seqList == NULL){printf("Malloc seqlist failed\n");return NULL;}seqList->data = (Element *) malloc(sizeof(Element) * n); //申请空间if (seqList->data == NULL){printf("Malloc data failed");return NULL;}seqList->len = 0;seqList->cap = n;return seqList;
}
测试代码:
int main() {SeqList *list = createSeqList(10);if (list == NULL) {printf("创建顺序表失败\n");return 1;}printf("顺序表创建成功\n");free(list->data);free(list);return 0;
}
测试结果:
3.2 releaseSeqList函数的实现
函数定义:
void releaseSeqList(SeqList *seqList); // 释放掉表头以及表内的数据
函数功能:
该函数用于释放一个顺序表所占用的内存空间,防止内存泄漏
实现思路:
a.先判断表头再判断数据域是否为空b.如果数据域不为空,则释放掉表内的数据域空间c.释放掉表头空间
具体实现代码:
void releaseSeqList(SeqList *seqList) {if (seqList){if (seqList->data){free(seqList->data);}free(seqList);printf("Release success!\n");}
}
测试代码:
int main() {SeqList *list = createSeqList(10);if (list == NULL) {printf("创建顺序表失败\n");return 1;}printf("顺序表创建成功\n");releaseSeqList(list); // 释放顺序表return 0;
}
测试结果:
3.3 enLargerSeq 函数的实现
函数定义:
int enLargerSeq(SeqList *seqList); // 扩容函数
函数功能:
该函数用于对顺序表进行扩容,将顺序表的大小扩展为原来的两倍
实现思路:
a.先申请一个新空间 tmp,新空间的大小为原来空间大小(seqList->len / seqList->cap)的两倍
b.将原来空间的值通过 for 循环赋值给新空间
c.释放掉原来表头指向的空间 seqList->data
d.将表头的 seqList->data 指向新申请的空间 tmp
e.将 seqList->cap 大小变为原来的两倍
具体实现代码:
int enLargerSeq(SeqList *seqList) {Element *tmp = (Element *) malloc(sizeof (Element) * 2 * seqList->cap);if (!tmp){printf("Enlarger element failed!\n");return -1;}for (int i = 0; i < seqList->len; i++){tmp[i] = seqList->data[i];}free(seqList->data); // 这两句是否有先后?seqList->data = tmp; // 有先后,如果调换顺序就会先指向新的内存空间,然后再释放老的内存空间,此时老的内存空间已经找不到了,会发生内存泄露seqList->cap *= 2;printf("Enlarge success!\n");return 0;
}
测试代码:
int main(){SeqList *list = createSeqList(5);for (int i = 0; i < 5; i++){pushBackSeq(list, i);}pushBackSeq(list,666);showSeqList(list);printf("%d", list->cap);return 0;
}
测试结果:
3.4 showSeqList && findSeq 函数的实现
函数定义:
void showSeqList(SeqList *seqList); // 遍历并显示数据int findSeq(SeqList *seqList, Element val); // 按值查找
函数功能:
1.showSeqList 函数
该函数用于遍历并显示顺序表中的所有数据
2.findSeq 函数
该函数用于在顺序表中查找指定的值,并返回该值的索引。如果未找到,则返回 -1
实现思路:
1.showSeqList 函数
a.首先判断传入的表是否为空
b.使用 for 循环遍历整个表并打印数据
2.findSeq 函数
a.判断传染的表是否为空且表的长度是否合法(seqList->len > 0)
b.使用for循环遍历表,如果找到了val就直接返回该数的下标
c.如果没找到,打印提示信息,返回-1
具体实现代码:
// showSeqList 函数
void showSeqList(SeqList *seqList) {if (seqList == NULL || seqList->data == NULL){printf("SeqList is null.Show error\n");return;}for (int i = 0; i < seqList->len; i++){printf("%d ", seqList->data[i]);}printf("\n");
}// findSeq 函数
int findSeq(SeqList *seqList, Element val) {if (!seqList || !seqList->data || seqList->len <= 0){printf("SeqList is null!\n");return -1;}for (int i = 0; i < seqList->len; i++){if (seqList->data[i] == val){return i;}}printf("Find value error!\n");return -1;
}
测试代码:
int main(){SeqList *list = createSeqList(5);for (int i = 0; i < 5; i++){pushBackSeq(list, i);}showSeqList(list);int index1 = findSeq(list, 666);int index2 = findSeq(list,0);printf("%d\n", index1);printf("%d\n", index2);releaseSeqList(list);return 0;
}
测试结果:
3.4 pushBackSeq && headInsertSeq && insertSeq 函数的实现
函数定义:
int pushBackSeq(SeqList *seqList, Element val); // 往尾部插入一个元素int headInsertSeq(SeqList *seqList, Element val); // 往头部插入一个元素int insertSeq(SeqList *seqList, int pos, Element val); // 按指定位置来插入元素
函数功能:
1.pushBackSeq 函数
在顺序表尾部插入一个元素
2.headInsertSeq 函数
在顺序表头部插入一个元素
3.insertSeq 函数
在顺序表指定位置插入一个元素
实现思路:
1.pushBackSeq 函数
a.判断传入的链表以及数据域是否为空b.判断当前元素是否到达空间上限,如果是则需要扩容c.往 seqList->len 的位置放入元素d.seqList->len++
2.headInsertSeq 函数
a.判断传入的链表以及数据域是否为空b.判断当前元素是否到达空间上限,如果是则需要扩容c.从第 1 个元素开始,依次往后移动元素d.往下标为 0 的位置放入元素e.seqList->len++
3.insertSeq 函数
a.判断传入的链表以及数据域是否为空b.判断当前元素是否到达空间上限,如果是则需要扩容c.从第 seqList->len-1 个元素开始,直到第 pos 个元素(包含),从后往前移动元素d.往下标为 pos 的位置放入元素e.seqList->len++
具体实现代码:
1.pushBackSeq 函数
// pushBackSeq 函数
int pushBackSeq(SeqList *seqList, Element val) {if (!seqList || !seqList->data){printf("Seqlist is null");return -1;}if (seqList->len >= seqList->cap){printf("Insertion overflow. Consider enlarging the structure!\n");if (enLargerSeq(seqList) == -1){printf("Enlarge failed!\n");return -1;}}seqList->data[seqList->len] = val;seqList->len++;return 0;
}
2.headInsertSeq 函数
// headInsertSeq 函数
int headInsertSeq(SeqList *seqList, Element val) {if (!seqList || !seqList->data){printf("Seqlist is null");return -1;}if (seqList->len >= seqList->cap){printf("Insertion overflow. Consider enlarging the structure!\n");if (enLargerSeq(seqList) == -1){printf("Enlarge failed!\n");return -1;}}for (int i = seqList->len; i > 0; i--) { // 核心移动逻辑seqList->data[i] = seqList->data[i-1];}seqList->data[0] = val;seqList->len++;return 0;
}
3.insertSeq 函数
// insertSeq 函数
int insertSeq(SeqList *seqList, int pos, Element val) {if (!seqList || !seqList->data){printf("Seqlist is null");return -1;}// 为什么是 > seqList->len,而不是 > seqList->cap?// 顺序表是一种线性数据结构,它要求元素在内存中连续存储。这意味着元素之间不能有空隙,必须紧密排列// 使用 len 而不是 cap 来确定插入位置,可以保证顺序表中的元素始终保持连续存储,这是顺序表的核心特征if (pos < 0 || pos > seqList->len){printf("Insert value out of bounds\n");return -1;}if (seqList->len >= seqList->cap){printf("Insertion overflow. Consider enlarging the structure!\n");if (enLargerSeq(seqList) == -1){printf("Enlarge failed!\n");return -1;}}for (int i = seqList->len-1; i >= pos; i--){ // 核心移动逻辑seqList->data[i+1] = seqList->data[i];}seqList->data[pos] = val;seqList->len++;return 0;
}
测试代码:
int main(){SeqList *list = createSeqList(5);for (int i = 0; i < 5; i++){pushBackSeq(list, i);}showSeqList(list);insertSeq(list, 5, 111); // 任意位置插入showSeqList(list);insertSeq(list, 0, 222); // 任意位置插入showSeqList(list);insertSeq(list, 999, 333); // 任意位置插入showSeqList(list);printf("\n");headInsertSeq(list, 888); // 头插法showSeqList(list);printf("\n");pushBackSeq(list, 666); // 尾插法showSeqList(list);releaseSeqList(list);return 0;
}
测试结果:
3.5 deleteSpeSeq && deleteHeadSeq && deleteTailSeq 函数的实现
函数定义:
int deleteSpeSeq(SeqList *seqList, Element val); // 删除特定值的元素int deleteHeadSeq(SeqList *seqList); // 删除首元素int deleteTailSeq(SeqList *seqList); // 删除尾元素
函数功能:
1.deleteSpeSeq 函数
该函数用于在顺序表中删除指定值的元素
2. deleteHeadSeq 函数
该函数用于删除顺序表中的首元素
3.deleteTailSeq 函数
该函数用于删除顺序表中的尾元素
实现思路:
1.deleteSpeSeq 函数
a.判断传入的表是否为空
b.调用 findSeq 函数,判断要查找的值是否在表中
c.如果不在表中,则返回错误标志
d.从 pos+1 的位置开始,从后往前覆盖元素
e.seqList->len--
2. deleteHeadSeq 函数
a.判断传入的表是否为空
b.判断表内是否有元素
c.从第二个元素(索引为1)开始,从后往前进行覆盖
d.seqList->len--
3.deleteTailSeq 函数
a.判断传入的表是否为空
b.判断表内是否有元素
d.seqList->len--
具体实现代码:
1.deleteSpeSeq 函数
int deleteSpeSeq(SeqList *seqList, Element val) {if (!seqList || !seqList->data){printf("Seqlist is null. Delete failed!\n");return -1;}int pos = findSeq(seqList, val);if (pos == -1){printf("Delete value error!\n");return -1;}for (int i = pos + 1; i < seqList->len; i++){seqList->data[i - 1] = seqList->data[i];}/*for (int i = pos; i < seqList->len; i++){ // 第二种写法可能会有溢出的风险seqList->data[i] = seqList->data[i + 1];}*/seqList->len--;return 0;
}
2. deleteHeadSeq 函数
int deleteHeadSeq(SeqList *seqList) {if (!seqList || !seqList->data){printf("Seqlist is null. Delete failed!\n");return -1;}if (seqList->len <= 0){printf("Delete head value failed!");return -1;}for (int i = 1; i < seqList->len; i++){seqList->data[i-1] = seqList->data[i];}seqList->len--;return 0;
}
3.deleteTailSeq 函数
int deleteTailSeq(SeqList *seqList) {if (!seqList || !seqList->data){printf("Seqlist is null. Delete failed!\n");return -1;}if (seqList->len <= 0){printf("Delete tail value failed!");return -1;}seqList->len--;return 0;
}
测试代码:
int main(){SeqList *list = createSeqList(5);for (int i = 0; i < 5; i++){pushBackSeq(list, i);}deleteHeadSeq(list);showSeqList(list);deleteTailSeq(list);showSeqList(list);deleteSpeSeq(list,999);showSeqList(list);deleteSpeSeq(list,1);showSeqList(list);releaseSeqList(list);return 0;
}