这里是只讲干货不讲废话的炽念,这个系列的文章是为了我自己以后复习数据结构而写,所以可能会用一种我自己能够听懂的方式来描述,不会像书本上那么枯燥和无聊,且全系列的代码均是可运行的代码,关键地方会给出注释^_^
全文近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;
}