C语言数据结构之顺序表(上)

前言:

        ⭐️此篇博文主要分享博主在学习C语言的数据结构之顺序表的知识点时写的笔记,若有错误,还请佬指出,一定感谢!制作不易,若觉得内容不错可以点赞👍+收藏❤️,这是对博主最大的认可!


顺序表的概念

        顺序表是用一段 物理地址连续的存储单元依次存储数据元素的线性结构,一般情况下采用数组存储。在数组上完成数据的增删查改。

顺序表的分类

1.静态顺序表:使用定长数组存储元素

2.动态顺序表:使用动态开辟的数组存储

静态顺序表的实现

1.前期准备

        a.建立工程,建立相应的文件。

        博主这边建立了三个文件,首先第一个SeqList.h这个头文件是用于接口(函数)的声明,SeqList.c这个.c文件是用于接口(函数)的实现,test.c文件主要用于测试相对应的接口与预期是否有差别。

        b.两个.c文件均包含SeqList.h

        c.浅浅的定义好结构体

typedef struct SeqList
{int arr[100];size_t size;//有效数据个数}SeqList;

        由于定义数组类型时只定义了int类型,这样的话只能存储int类型的数据了。为了方便接收其他数据时,改动较少关于int的地方,故可对int类型重定义。而且100把这个数组的大小写死了,故可用#define定义的常变量来替代100,方便后续对数组大小进行修改。(有时不止仅仅是修改int arr[100]里面的100,而是整个工程中用到数组大小为100的地方都需要修改,这样修改较麻烦。)还有一点是要使用size_t类型(vs2022下是无符号整型)需包含头文件stdio.h。

改动如下:

typedef int Datatype;
#define N 100typedef struct SeqList
{Datatype arr[N];size_t size;//有效数据个数}SeqList;

注:用到int的地方都可以用Datatype替代,要接收其他数据类型时,只需把typedef int Datatype中的int改为其他类型即可,数组大小100也是如此。

2.接口的实现

.h文件:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>typedef int Datatype;
#define N 100typedef struct SeqList
{Datatype arr[N];size_t size;//有效数据个数}SeqList;//顺序表的初始化
void InitSeqList();//顺序表的展示
void ShowSeqList();//顺序表的销毁
void DestorySeqList();//顺序表的增加数据功能
void AddData();//顺序表的删除数据功能
void DeleteData();//顺序表的查找功能
void FindData();//顺序表的修改功能
void ModifyData();

🤔思考:参数传值还是传址?

😶凡是需要对对象操作的都应当传地址,因为有对象的地址才能找到对象那片空间,对空间进行操作。所以这里为了统一形参形式,我都选择了用指针接收。由于对象是个结构体所以用结构体指针当形参。

//顺序表的初始化
void InitSeqList(SeqList*);//顺序表的展示
void ShowSeqList(SeqList*);//顺序表的销毁
void DestorySeqList(SeqList*);//顺序表的增加数据功能
void AddData(SeqList*, Datatype);//顺序表的删除数据功能
void DeleteData(SeqList*, Datatype);//顺序表的查找功能
void FindData(SeqList*);//顺序表的修改功能
void ModifyData(SeqList*, Datatype, Datatype);

SeqList.c文件:

先来实现第一个接口//顺序表的初始化---void InitSeqList(SeqList*)
//顺序表的初始化
void InitSeqList(SeqList* p)
{//对数组进行初始化for (size_t i = 0; i < N; i++){p->arr[i] = -1;//初始化为-1,这一步可不做}p->size = 0;//有效数据个数置为0
}

一个一个功能来测试:

调试发现确实和预期的效果一样。

再来实现第二个接口//顺序表的销毁---void destorySeqList(SeqList*)
//顺序表的销毁
void DestorySeqList(SeqList* p)
{p->size = 0;
}

这个和初始化差不多,可做可不做。

再来实现第三个接口//顺序表的增加数据---void AddData(SeqList*, Datatype)
//顺序表的增加数据功能
void AddData(SeqList* p, Datatype x)
{p->arr[p->size] = x;p->size++;//p->arr[p->size++] = x;
}

功能测试(调试):

☑️这里对op这个结构体增加数据,确实能发现数组元素增加了5个,size也跟着更新了。

再来实现第四个接口//顺序表的展示void ShowSeqList(SeqList*)
//顺序表的展示
void ShowSeqList(SeqList* p)
{for (size_t i = 0; i < p->size; i++){printf("%d ", p->arr[i]);}printf("\n");//换个行,没别的意思
}

☑️ok,没啥问题。

再来实现第五个接口//顺序表的删除数据功能void DeleteData(SeqList*, Datatype)
//顺序表的删除数据功能
void DeleteData(SeqList* p, Datatype x)
{for (size_t i = 0; i < N; i++)//遍历寻找{if (p->arr[i] == x)//找到了,i是对对应的下标{//挪动覆盖即可for (size_t j = i + 1; j < p->size; j++){p->arr[j - 1] = p->arr[j];}p->size--;}}
}

☑️下标为4那个5不在size的有效数据范围内,打印时不会显示出它。

最后来实现第六个接口//顺序表的查找功能void FindData(SeqList*)和第七个接口//顺序表的修改功能

查找过程在删除那里结合在一起写了,就不再重复写了,应该是博主不小心把删除和查找的参数写反了,本来删除应该是实现顺序表的尾删。不过问题不大,静态顺序表不是重点,实际应用中也不多。

//顺序表的修改功能
void ModifyData(SeqList* p, Datatype x, Datatype y)
{for (size_t i = 0; i < N; i++)//遍历寻找{if (p->arr[i] == x)//找到了,i是对对应的下标{p->arr[i] = y;//把对应的值改掉即可}}
}

☑️这里先增加了5这个数据,然后顺序表中有2个5,我改成了0,如果只想改第一个5,可以在Modify函数的if语句后加break;语句,找到一个就修改然后结束循环。

3.完整代码展示

.h文件:

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>typedef int Datatype;
#define N 100typedef struct SeqList
{Datatype arr[N];size_t size;//有效数据个数}SeqList;//顺序表的初始化
void InitSeqList(SeqList*);//顺序表的展示
void ShowSeqList(SeqList*);//顺序表的销毁
void DestorySeqList(SeqList*);//顺序表的增加数据功能
void AddData(SeqList*, Datatype);//顺序表的删除数据功能
void DeleteData(SeqList*, Datatype);//顺序表的查找功能
void FindData(SeqList*);//顺序表的修改功能
void ModifyData(SeqList*, Datatype, Datatype);

SeqList.c接口功能的实现:

#include "SeqList.h"//顺序表的初始化
void InitSeqList(SeqList* p)
{//对数组进行初始化for (size_t i = 0; i < N; i++){p->arr[i] = -1;//初始化为-1,这一步可不做}p->size = 0;//有效数据个数置为0
}//顺序表的销毁
void DestorySeqList(SeqList* p)
{p->size = 0;
}//顺序表的增加数据功能
void AddData(SeqList* p, Datatype x)
{//p->arr[p->size] = x;//p->size++;p->arr[p->size++] = x;
}//顺序表的展示
void ShowSeqList(SeqList* p)
{for (size_t i = 0; i < p->size; i++){printf("%d ", p->arr[i]);}printf("\n");//换个行,没别的意思
}//顺序表的删除数据功能
void DeleteData(SeqList* p, Datatype x)
{for (size_t i = 0; i < N; i++)//遍历寻找{if (p->arr[i] == x)//找到了,i是对对应的下标{//挪动覆盖即可for (size_t j = i + 1; j < p->size; j++){p->arr[j - 1] = p->arr[j];}p->size--;}}
}//顺序表的修改功能
void ModifyData(SeqList* p, Datatype x, Datatype y)
{for (size_t i = 0; i < N; i++)//遍历寻找{if (p->arr[i] == x)//找到了,i是对对应的下标{p->arr[i] = y;//把对应的值改掉即可}}
}

测试代码.c文件

#include "SeqList.h"void test1(SeqList* p)
{InitSeqList(p);AddData(p, 1);AddData(p, 2);AddData(p, 3);AddData(p, 4);AddData(p, 5);ShowSeqList(p);DeleteData(p, 3);ShowSeqList(p);AddData(p, 5);ShowSeqList(p);ModifyData(p, 5, 0);ShowSeqList(p);}int main()
{SeqList op;test1(&op);return 0;
}

4.小结

        以上就是博主在学习数据结构中以自己的构思写的静态顺序表,写法不唯一,但静态顺序表在实际开发中并不怎么被使用,原因在于太固定了,不够灵活,要么数组大小太大,浪费空间;要么数组太小,存不下太多东西。

动态顺序表的实现

1.前期准备

a.创建工程时,和静态顺序表一样,这里不过多赘述。
b.浅浅定义好结构体
typedef int Datatype;typedef struct SeqList
{Datatype* arr;//指针管理开辟的空间size_t size;//有效数据的个数size_t Capacity;//容量大小
}SeqList;

2.接口的实现

.h文件:

//顺序表的初始化
void InitSeqList();//顺序表的扩容
void CheckCapacity();//顺序表的展示
void PrintSeqList();//顺序表的尾插
void PushBack();//顺序表的尾删
void PopBack();//顺序表的头插
void PushFront();//顺序表的头删
void PopFront();//顺序表的查找
void FindSeqList();//统计出个数
size_t FindSeqList1();//返回首先找到的元素的下标//顺序表任意位置的插入
void InsertSeqList();//插入到当前下标位置的后面一个位置
void InsertSeqList1();//插入到当前下标位置//顺序表任意位置的删除
void EraseSeqList();
//顺序表任意元素的删除
void EraseSeqList1();//顺序表任意位置的修改
void ModifySeqList();
//顺序表任意元素的修改
void ModifySeqList1();//顺序表的销毁
void DestorySeqList();

🤔思考:参数传值还是传址?

SeqList.c文件:

//顺序表的初始化void InitSeqList()
//顺序表的初始化
void InitSeqList(SeqList* p)
{assert(p);//这个可写可不写,因为操作系统生成p这个变量还是有空间的p->arr = NULL;p->size = 0;p->Capacity = 0;
}

☑️发现确实初始化成功。

//顺序表的扩容void CheckCapacity();
//顺序表的扩容
void CheckCapacity(SeqList* p)
{assert(p);if (p->size == p->Capacity)//扩容判断条件,这里选扩容两倍{SeqList* tem = (SeqList*)realloc(p->arr, 2 * p->Capacity * (sizeof(Datatype)));if (tem == NULL)//扩容失败{perror("reallc fail\n");//打印扩容失败的原因exit(-1);//退出程序}else//扩容成功{p->arr = tem;p->Capacity *= 2;//更新容量大小tem = NULL;//tem指针为局部变量可置空可不置}}
}

🤔思考:有啥问题没有?

😶明显初始化的时候,给的容量和有效数据个数都为0,这里不做处理的话就会出bug。

注:局部变量tem不置空,出了函数体tem一样会被销毁。调用assert函数记得引用头文件assert.h;调用realloc函数和记得引用头文件stdlib.h。

☑️扩容函数确实有问题

改为:

//顺序表的扩容
void CheckCapacity(SeqList* p)
{assert(p);if (p->size == p->Capacity)//扩容判断条件,这里选扩容两倍{if (p->Capacity == 0)//先考虑容量为0的特殊情况{p->Capacity = 2;}SeqList* tem = (SeqList*)realloc(p->arr, 2 * p->Capacity * (sizeof(Datatype)));if (tem == NULL)//扩容失败{perror("reallc fail\n");//打印扩容失败的原因exit(-1);//退出程序}else//扩容成功{p->arr = tem;p->Capacity *= 2;//更新容量大小tem = NULL;//tem指针为局部变量可置空可不置}}
}

☑️good,处理容量为0的情况(这个根据初始化容量大小给的多少,我这里是初始化的时候容量给0了,所以在扩容的时候要优先考虑容量为0的情况,如果你不是扩容几倍的话也不用考虑容量是否为0的情况),不然会产生bug。

//顺序表的尾插void PushBack()
//顺序表的尾插
void PushBack(SeqList* p, Datatype x)
{assert(p);CheckCapacity(p);//检查容量,确保容量大于有效数据个数,保证能插入数据//p->arr[p->size] = x;//p->size++;p->arr[p->size++] = x;
}

☑️good,不仅尾插成功了,而且我插入5个数据,容量也确实扩大了两倍!完美符合预期。

//顺序表的尾删void PopBack()
//顺序表的尾删
void PopBack(SeqList* p)
{assert(p);p->size--;//有效个数-1即可
}

🤔思考:有啥问题没有?

😶万一有效数据个数size已经为0了呢?再--,又因为size是个size_t类型(无符号整型),都不知道减哪里去了哈哈哈。

☑️千万别小瞧了这个哦,因为在打印顺序表的时候,是不是要根据有效数据个数size来确定打印几个数据?这个时候size这么大?但是容量也就是开辟的空间有那么大吗?越界那么大,直接程序就崩了。下面给你验证一下VS2022会给出啥错误提示吧。

改为:

//顺序表的尾删
void PopBack(SeqList* p)
{assert(p);if (p->size == 0)//处理一下有效个数为0的情况{printf("顺序表已为空,无需删除\n");//提示一下return;}p->size--;//有效个数-1即可
}

☑️Ok,符合预期。

//顺序表的展示void PrintSeqList()
//顺序表的展示
void PrintSeqList(SeqList* p)
{assert(p);for (size_t i = 0; i < p->size; i++)//打印这size个有效数据个数{printf("%d ", p->arr[i]);}printf("\n");//无别的意思,换个行,方便观察
}

🤔思考:size = 0时,会有影响吗?

😶size = 0时,不进循环,不打印数据,没影响,但是我想提示一下size = 0的情况。

改为:

//顺序表的展示
void PrintSeqList(SeqList* p)
{assert(p);if (p->size == 0){printf("NULL");//提示一下}for (size_t i = 0; i < p->size; i++)//打印这size个有效数据个数{printf("%d ", p->arr[i]);}printf("\n");//无别的意思,换个行,方便观察
}

🤔思考:程序为啥崩了呢?

😶哈哈哈,调试给你看看:

定位这条语句,啥意思呢?翻译过来就是读写发生冲突。那为啥会这样呢?我们来看realloc的参数,p->arr,思考arr我们初始化了吗?那它的值是个随机值,被当做地址处理了,但是那块地址我们又没申请,怎么可以通过p对arr解引用访问呢?

吓得博主赶紧去初始化。

☑️okk,再去试试把数据删空,看是否能打印我对于数据个数为0的处理。

☑️ferfect,跟预期一毛一样。

补充:对于尾删不处理有效个数为0的情况,试试尾删多了,打印的时候会出什么bug

果然崩了,对于size_t类型的size,当size = 0时,size--,这个数会反向增大,结果就是size远大于容量,这肯定就崩了。从这里结合上述未初始化顺序表还能看出当发生读写冲突时大概就是数组越界了。(对于越界一点点VS会报错误,比如int arr[10],你去访问arr[10],这时候访问越界,VS会报错误,但是对于arr[1000],能运行起来,但是程序会崩了,体现在:...已退出,(退出)代码为一堆乱七八糟的数字,不是正常的return 0,这个退出是程序崩了的退出,调试上表现为发生异常,读写访问冲突。这里博主就不证明了哈,不是本篇的重点。)

//顺序表的头插void PushFront()

数据结构这里比较重要的一点是画好逻辑图。

挪动覆盖,把i-i位置上的数据copy到i对应的位置上,也可以下面这样Ovo

这里我用第一种吧。

//顺序表的头插
void PushFront(SeqList* p, Datatype x)
{assert(p);CheckCapacity(p);//检查容量,确保容量大于有效数据个数,保证能插入数据//挪动覆盖for (size_t i = p->size; i > 0; i--){p->arr[i] = p->arr[i - 1];}//插入数据p->arr[0] = x;//更新有效数据个数p->size++;
}

☑️顺序表没数据时也完全可以插入,不会出bug,因为不进for循环挪动且容量大于当前有效数据个数,保证能插入数据,只不过size只需要更新一下即可。使用第二种那可就坑太多了!建议踩踩。

//顺序表的头删void PopFront()

先不着急写代码,先把逻辑理清。

这次我要选择第二种了!因为i从下标为1开始的话,万一顺序表无数据呢?那不就越界了吗?

//顺序表的头删
void PopFront(SeqList* p)
{assert(p);//挪动覆盖for (size_t i = 0; i < p->size - 1; i++){p->arr[i] = p->arr[i + 1];}//更新有效数据个数p->size--;
}

这是头删五次的运行结果,那如果五次以上呢?也就是说数据空了,我接着删!

这就是程序崩了导致的退出,来看看因为啥崩了。

好好好,又是因为越界了,哈哈哈,为啥呢?一步一步(F11)调试的时候会发现居然size = 0的时候,循环竟然还进去了!为啥会进去?难道i = 0的时候小于p->size - 1了?嗯,没错!因为p->size是个size_t类型的变量,size-1时,你猜它等于几?没错,又是那个巨大的数!

于是我试着强转

结果还是能进循环(意味着判断条件0<-1居然进循环了?)这里引入一下大佬的文章size_t与int进行比较时的陷阱_vector的size无法和int进行比较_arccosY的博客-CSDN博客

确实这里比较的时候有陷阱。okk回归重点!即使这样p->size = 0,再跳过循环size--,不还是会在打印的时候出bug吗?

☑️该崩还得崩,主要原因是要去判断没数据时就不用--了。

改为:

//顺序表的头删
void PopFront(SeqList* p)
{assert(p);//if (p->size == 0)//{//	printf("顺序表已空,无须删除\n");//提示一下//	return;//}if (p->size > 0)//有数据才能--{//挪动覆盖for (size_t i = 0; i < p->size - 1; i++){p->arr[i] = p->arr[i + 1];}//更新有效数据个数p->size--;}
}

☑️这两种改法都ok。

//顺序表的查找void FindSeqList()//统计出个数
//顺序表的查找
void FindSeqList(SeqList* p, Datatype x)//统计出个数
{assert(p);//遍历寻找是否有要查找的元素size_t i;size_t count = 0;//记录有几个这样的数for (i = 0; i < p->size; i++){if (p->arr[i] == x){count++;}}if (count == 0)//遍历完没找到要查找的数据而退出循环{printf("你要查找的元素是%d,共有%d个,下标分别是:", x, count);}else//表示在size个数据前找到跳出的循环而不是遍历完了没找到退出循环{printf("共有%d个,下标分别是:", count);for (size_t i = 0; i < p->size; i++){if (p->arr[i] == x){printf("%d ", i);}}printf("\n");//换个行,方便观察}
}

//size_t FindSeqList1();//返回首先找到的元素的下标
size_t FindSeqList1(SeqList* p, Datatype x)//返回首先找到的元素的下标
{assert(p);//遍历寻找size_t i;for (i = 0; i < p->size; i++){if (p->arr[i] == x){return i;//返回首个查找到的元素的下标}}if (i == p->size)//遍历完数组没找到元素而退出的循环{return -1;//没找到}
}

🤔思考:这样有问题没?

😶肯定有问题啊!返回-1怎么能被函数的返回类型size_t接收呢?

所以把size_t改为int即可。

//顺序表任意位置的插入void InsertSeqList()//插入到当前下标位置的后面一个位置

//顺序表任意位置的插入
void InsertSeqList(SeqList* p, size_t pos, Datatype x)//插入到当前下标位置的后面一个位置
{assert(p);CheckCapacity(p);//将pos位置后面的数据挪动覆盖for (size_t i = p->size - 1; i > pos; i++){p->arr[i + 1] = p->arr[i];}//插入数据到pos位置后面p->arr[pos + 1] = x;//更新sizep->size++;
}

好像成功了哦,但毕竟是好像!

crazy!i = 184467......居然在判断条件的时候小于pos(0)跳出了循环,但这还不是更要命的!最重要的是它插到size后面去了!怎么打印出来呢?这才是最要命的!表明上没bug其实调试一看,有很大的bug。

改正:

//顺序表任意位置的插入
void InsertSeqList(SeqList* p, size_t pos, Datatype x)//插入到当前下标位置的后面一个位置
{assert(p);CheckCapacity(p);if (p->size > 0 && p->size > pos)//在数据的后面插入的前提是有数据&&在某个数据后面插入,那他的下标肯定小于有效数据的个数{//将pos位置后面的数据挪动覆盖for (size_t i = p->size - 1; i > pos; i++){p->arr[i + 1] = p->arr[i];}//插入数据到pos位置后面p->arr[pos + 1] = x;//更新sizep->size++;}else if (p->size <= pos)//这种p->size等不等于0都一样{if (p->size == 0 && p->size == pos)//这种特殊情况就尾插{PushBack(p, x);}elseprintf("无效插入!\n");}
}

这个是最开始写的,有点bug后来改为了上面那种。

一一测试一下,防止有bug

就是设置值去把自己的逻辑都走一遍,看是否跟自己的代码逻辑一致。

也是完全ok,和自己想要的功能一样。

void InsertSeqList1()//插入到当前下标位置

单纯只是想退出循环的时候是i = pos,所以不用i和i+1。

void InsertSeqList1(SeqList* p, size_t pos, Datatype x)//插入到当前下标位置
{assert(p);CheckCapacity(p);if (p->size >= pos)//保证插入的数据下标小于等于size{if (p->size == 0 && p->size == pos)//特殊情况,无数据时的插入{p->arr[pos] = x;p->size++;}else if (p->size > pos)//我这里设置只能插size - 1之前的,因为有size个数据{for (size_t i = p->size; i > pos; i--)//挪动覆盖{p->arr[i] = p->arr[i - 1];}p->arr[pos] = x;p->size++;}}elseprintf("无效插入\n");
}

也可以在else if后再加一条else if (p->size == pos)        printf("无效插入\n”);//提示一下。

//顺序表任意位置的删除void EraseSeqList()

//顺序表任意位置的删除
void EraseSeqList(SeqList* p, size_t pos)
{assert(p);//挪动覆盖即可if (p->size > 0 && p->size >= pos)//有数据才能这样挪{for (size_t i = pos; i < p->size - 1; i++){p->arr[i] = p->arr[i + 1];}//更新sizep->size--;}
}

这里就不写pos>size的情况打印提示:无效数据的删除了。

//顺序表任意元素的删除void EraseSeqList1()
//顺序表任意元素的删除
void EraseSeqList1(SeqList* p, Datatype x)
{//这里联系前面的查找返回下标写一个,效率会低一点assert(p);int ret = FindSeqList1(p, x);//遍历一遍的情况,接收暗号if (ret == -1)//遍历一遍还没找到就没有该元素{printf("顺序表无该元素\n");return;}while (ret != -1)//这是遍历一遍至少有一个该元素{EraseSeqList(p, ret);ret = FindSeqList1(p, x);//找下一个下标}
}

//顺序表任意位置的修改void ModifySeqList()
//顺序表任意位置的修改
void ModifySeqList(SeqList* p, size_t pos, Datatype x)
{if (p->size == 0 || p->size <= pos)//pos肯定要在最大下标之前{printf("该位置无数据,无需修改\n");return;}p->arr[pos] = x;//修改
}

//顺序表任意元素的修改void ModifySeqList1()
//顺序表任意元素的修改
void ModifySeqList1(SeqList*p, Datatype x, Datatype y)
{assert(p);int ret = FindSeqList1(p, x);//遍历一遍看能找得到不if (ret == -1){printf("该位置无数据,无需修改\n");return;}while (ret != -1){ModifySeqList(p, ret, y);//找到下标修改ret = FindSeqList1(p, x);//找下一个下标}
}

注:以上的任意元素的修改和删除并不是最优效率!只是为了联系我写的那些函数。

//顺序表的销毁void DestorySeqList()

所以别单纯的把arr置空,而是应该先把arr指向的那块空间显free掉。

//顺序表的销毁
void DestorySeqList(SeqList* p)
{free(p->arr);p->arr = NULL;p->Capacity = p->size = 0;//置空即可
}

3.完整代码展示

.h文件

#pragma once
#define _CRT_SECURE_NO_WARNINGS 1#include <stdio.h>
#include <assert.h>
#include <stdlib.h>typedef int Datatype;typedef struct SeqList
{Datatype* arr;//指针管理开辟的空间size_t size;//有效数据的个数size_t Capacity;//容量大小
}SeqList;//顺序表的初始化
void InitSeqList(SeqList*);//顺序表的扩容
void CheckCapacity(SeqList*);//顺序表的展示
void PrintSeqList(SeqList*);//顺序表的尾插
void PushBack(SeqList*, Datatype);//顺序表的尾删
void PopBack(SeqList*);//顺序表的头插
void PushFront(SeqList*, Datatype);//顺序表的头删
void PopFront(SeqList*);//顺序表的查找
void FindSeqList(SeqList*, Datatype);//统计出个数
int FindSeqList1(SeqList*, Datatype);//返回首先找到的元素的下标//顺序表任意位置的插入
void InsertSeqList(SeqList*, size_t, Datatype);//插入到当前下标位置的后面一个位置
void InsertSeqList1(SeqList*, size_t, Datatype);//插入到当前下标位置//顺序表任意位置的删除
void EraseSeqList(SeqList*, size_t);
//顺序表任意元素的删除
void EraseSeqList1(SeqList*, Datatype);//顺序表任意位置的修改
void ModifySeqList(SeqList*, size_t, Datatype);
//顺序表任意元素的修改
void ModifySeqList1(SeqList*, Datatype, Datatype);//顺序表的销毁
void DestorySeqList(SeqList*);

SeqList.c接口功能的实现:

#include "SeqList.h"//顺序表的初始化
void InitSeqList(SeqList* p)
{assert(p);p->arr = NULL;p->size = 0;p->Capacity = 0;
}//顺序表的扩容
void CheckCapacity(SeqList* p)
{assert(p);if (p->size == p->Capacity)//扩容判断条件,这里选扩容两倍{if (p->Capacity == 0)//先考虑容量为0的特殊情况{p->Capacity = 2;}SeqList* tem = (SeqList*)realloc(p->arr, 2 * p->Capacity * (sizeof(Datatype)));if (tem == NULL)//扩容失败{perror("reallc fail\n");//打印扩容失败的原因exit(-1);//退出程序}else//扩容成功{p->arr = tem;p->Capacity *= 2;//更新容量大小tem = NULL;//tem指针为局部变量可置空可不置}}
}//顺序表的尾插
void PushBack(SeqList* p, Datatype x)
{assert(p);CheckCapacity(p);//检查容量,确保容量大于有效数据个数,保证能插入数据//p->arr[p->size] = x;//p->size++;p->arr[p->size++] = x;
}//顺序表的尾删
void PopBack(SeqList* p)
{assert(p);//if (p->size == 0)//处理一下有效个数为0的情况//{//	printf("顺序表已为空,无需删除\n");//提示一下//	return;//}p->size--;//有效个数-1即可
}//顺序表的展示
void PrintSeqList(SeqList* p)
{assert(p);if (p->size == 0){printf("NULL");//提示一下}for (size_t i = 0; i < p->size; i++)//打印这size个有效数据个数{printf("%d ", p->arr[i]);}printf("\n");//无别的意思,换个行,方便观察
}//顺序表的头插
void PushFront(SeqList* p, Datatype x)
{assert(p);CheckCapacity(p);//检查容量,确保容量大于有效数据个数,保证能插入数据//挪动覆盖for (size_t i = p->size; i > 0; i--){p->arr[i] = p->arr[i - 1];}//插入数据p->arr[0] = x;//更新有效数据个数p->size++;
}//顺序表的头删
void PopFront(SeqList* p)
{assert(p);if (p->size == 0){printf("顺序表已空,无须删除\n");//提示一下return;}//if (p->size > 0)//有数据才能--//{//挪动覆盖for (size_t i = 0; i < p->size - 1; i++){p->arr[i] = p->arr[i + 1];}//更新有效数据个数p->size--;//}
}//顺序表的查找
void FindSeqList(SeqList* p, Datatype x)//统计出个数
{assert(p);//遍历寻找是否有要查找的元素size_t i;size_t count = 0;//记录有几个这样的数for (i = 0; i < p->size; i++){if (p->arr[i] == x){count++;}}if (count == 0)//遍历完没找到要查找的数据而退出循环{printf("顺序表无该元素\n");//提示一下}else//表示在size个数据前找到跳出的循环而不是遍历完了没找到退出循环{printf("你要查找的元素是%d,共有%d个,下标分别是:", x, count);for (size_t i = 0; i < p->size; i++){if (p->arr[i] == x){printf("%d ", i);}}printf("\n");//换个行,方便观察}
}
int FindSeqList1(SeqList* p, Datatype x)//返回首先找到的元素的下标
{assert(p);//遍历寻找size_t i;for (i = 0; i < p->size; i++){if (p->arr[i] == x){return i;//返回首个查找到的元素的下标}}if (i == p->size)//遍历完数组没找到元素而退出的循环{return -1;//没找到}
}//顺序表任意位置的插入
void InsertSeqList(SeqList* p, size_t pos, Datatype x)//插入到当前下标位置的后面一个位置
{assert(p);CheckCapacity(p);if (p->size > 0 && p->size > pos)//在数据的后面插入的前提是有数据&&在某个数据后面插入,那他的下标肯定小于有效数据的个数{//将pos位置后面的数据挪动覆盖for (size_t i = p->size - 1; i > pos; i++){p->arr[i + 1] = p->arr[i];}//插入数据到pos位置后面p->arr[pos + 1] = x;//更新sizep->size++;}else if (p->size <= pos)//这种p->size等不等于0都一样{if (p->size == 0 && p->size == pos)//这种特殊情况就尾插{PushBack(p, x);}elseprintf("无效插入!\n");}
}
void InsertSeqList1(SeqList* p, size_t pos, Datatype x)//插入到当前下标位置
{assert(p);CheckCapacity(p);if (p->size >= pos)//保证插入的数据下标小于等于size{if (p->size == 0 && p->size == pos)//特殊情况,无数据时的插入{p->arr[pos] = x;p->size++;}else if (p->size > pos)//我这里设置只能插size - 1之前的,因为有size个数据{for (size_t i = p->size; i > pos; i--)//挪动覆盖{p->arr[i] = p->arr[i - 1];}p->arr[pos] = x;p->size++;}}elseprintf("无效插入\n");
}//顺序表任意位置的删除
void EraseSeqList(SeqList* p, size_t pos)
{assert(p);//挪动覆盖即可if (p->size > 0 && p->size >= pos)//有数据才能这样挪{for (size_t i = pos; i < p->size - 1; i++){p->arr[i] = p->arr[i + 1];}//更新sizep->size--;}
}
//顺序表任意元素的删除
void EraseSeqList1(SeqList* p, Datatype x)
{//这里联系前面的查找返回下标写一个,效率会低一点assert(p);int ret = FindSeqList1(p, x);//遍历一遍的情况,接收暗号if (ret == -1)//遍历一遍还没找到就没有该元素{printf("顺序表无该元素\n");return;}while (ret != -1)//这是遍历一遍至少有一个该元素{EraseSeqList(p, ret);ret = FindSeqList1(p, x);//找下一个下标}
}//顺序表任意位置的修改
void ModifySeqList(SeqList* p, size_t pos, Datatype x)
{if (p->size == 0 || p->size <= pos)//pos肯定要在最大下标之前{printf("该位置无数据,无需修改\n");return;}p->arr[pos] = x;//修改
}
//顺序表任意元素的修改
void ModifySeqList1(SeqList*p, Datatype x, Datatype y)
{assert(p);int ret = FindSeqList1(p, x);//遍历一遍看能找得到不if (ret == -1){printf("该位置无数据,无需修改\n");return;}while (ret != -1){ModifySeqList(p, ret, y);//找到下标修改ret = FindSeqList1(p, x);//找下一个下标}
}//顺序表的销毁
void DestorySeqList(SeqList* p)
{free(p->arr);p->arr = NULL;p->Capacity = p->size = 0;//置空即可
}

测试代码.c文件

#include "SeqList.h"void test1(SeqList* p)
{InitSeqList(p);//CheckCapacity(p);PushBack(p, 1);PushBack(p, 2);PushBack(p, 3);PushBack(p, 4);PushBack(p, 5);PopBack(p);PopBack(p);PopBack(p);PopBack(p);PopBack(p);PopBack(p);PrintSeqList(p);
}void test2(SeqList* p)
{InitSeqList(p);PushBack(p, 1);PushBack(p, 2);PushBack(p, 3);PushBack(p, 4);PushBack(p, 5);PrintSeqList(p);PopBack(p);PrintSeqList(p);PopBack(p);PrintSeqList(p);PopBack(p);PrintSeqList(p);PopBack(p);PrintSeqList(p);PopBack(p);PrintSeqList(p);
}void test3(SeqList* p)
{InitSeqList(p);PushFront(p, 5);PrintSeqList(p);PushFront(p, 4);PrintSeqList(p);PushFront(p, 3);PrintSeqList(p);PushFront(p, 2);PrintSeqList(p);PushFront(p, 1);PrintSeqList(p);PopFront(p);PrintSeqList(p);PopFront(p);PrintSeqList(p);PopFront(p);PrintSeqList(p);PopFront(p);PrintSeqList(p);PopFront(p);PrintSeqList(p);PopFront(p);//第六次头删,对着空顺序表删PrintSeqList(p);}void test4(SeqList* p)
{InitSeqList(p);PushBack(p, 1);PrintSeqList(p);PushBack(p, 2);PrintSeqList(p);PushBack(p, 3);PrintSeqList(p);PushBack(p, 3);PrintSeqList(p);PushBack(p, 4);PrintSeqList(p);FindSeqList(p, 0);FindSeqList(p, 1);FindSeqList(p, 3);FindSeqList(p, 4);int a = FindSeqList1(p, 0);int b = FindSeqList1(p, 1);int c = FindSeqList1(p, 3);int d = FindSeqList1(p, 4);
}void test5(SeqList* p)
{InitSeqList(p);InsertSeqList(p, 0, 0);PrintSeqList(p);InsertSeqList(p, 1, 0);PrintSeqList(p);InsertSeqList(p, 3, 0);PrintSeqList(p);InsertSeqList(p, 0, 1);PrintSeqList(p);InsertSeqList(p, 1, 2);PrintSeqList(p);
}void test6(SeqList* p)
{InitSeqList(p);InsertSeqList1(p, 0, 0);//无数据时的插入PrintSeqList(p);InsertSeqList1(p, 0, -1);//头插PrintSeqList(p);InsertSeqList1(p, 1, 1);//中间插PrintSeqList(p);InsertSeqList1(p, 3, 2);//假尾插PrintSeqList(p);InsertSeqList1(p, 4, 2);//无效插入PrintSeqList(p);
}void test7(SeqList* p)
{InitSeqList(p);EraseSeqList(p, 0);//为空的时候删删试试会不会出bugPrintSeqList(p);PushBack(p, 1);//造几组数据PushBack(p, 2);PushBack(p, 3);PushBack(p, 4);PushBack(p, 5);PrintSeqList(p);EraseSeqList(p, 0);//头删PrintSeqList(p);EraseSeqList(p, 3);//尾删PrintSeqList(p);EraseSeqList(p, 1);//中间删PrintSeqList(p);}void test8(SeqList* p)
{InitSeqList(p);PushBack(p, 1);//造元素PushBack(p, 2);PushBack(p, 3);PushBack(p, 3);PushBack(p, 4);PushBack(p, 4);PushBack(p, 4);PushBack(p, 5);PrintSeqList(p);EraseSeqList1(p, 0);//验证没找到的暗号EraseSeqList1(p, 1);//头删PrintSeqList(p);EraseSeqList1(p, 3);//群删PrintSeqList(p);}void test9(SeqList* p)
{InitSeqList(p);ModifySeqList(p, 0, 0);//无数据时PushBack(p, 1);//造数据PushBack(p, 2);PushBack(p, 3);PushBack(p, 4);PushBack(p, 5);PrintSeqList(p);ModifySeqList(p, 5, 0);//pos == sizeModifySeqList(p, 0, 0);//头修PrintSeqList(p);ModifySeqList(p, 4, 0);//尾修PrintSeqList(p);ModifySeqList(p, 2, 0);//中间修PrintSeqList(p);}void test10(SeqList* p)
{InitSeqList(p);ModifySeqList1(p, 0, 1);//无元素时的试探//造数据PushBack(p, 1);PushBack(p, 2);PushBack(p, 3);PushBack(p, 3);PushBack(p, 5);PrintSeqList(p);ModifySeqList1(p, 1, -1);//修改头PrintSeqList(p);ModifySeqList1(p, 3, -3);//修改群PrintSeqList(p);DestorySeqList(p);
}int main()
{SeqList op;//test1(&op);//test2(&op);//test3(&op);//test4(&op);//test5(&op);//test6(&op);//test7(&op);//test8(&op);//test9(&op);test10(&op);return 0;
}

4.小结

        以上就是博主分享的顺序表以及在敲代码中间遇到的一些常见问题和易错的地方,由于篇幅问题,后面还有一些遇到的问题就不展示了。其实用int类型来代替size_t类型可能会简单挺多,但我们始终要明白,锻炼自己的调试能力也是提高自己代码能力的一个重要途径。感谢各位观看,祝大家在学习C语言数据结构的道路上越走越远!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/204578.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于C#实现并查集

一、场景 有时候我们会遇到这样的场景&#xff0c;比如:M{1,4,6,8},N{2,4,5,7}&#xff0c;我的需求就是判断{1,2}是否属于同一个集合&#xff0c;当然实现方法有很多&#xff0c;一般情况下&#xff0c;普通青年会做出 O(MN)的复杂度&#xff0c;那么有没有更轻量级的复杂度呢…

完美解决k8s master节点无法ping node节点中的IP或Service NodePort的IP

1、问题一 使用搭建好了K8S集群&#xff0c;先是node节点加入k8s集群时&#xff0c;用的内网IP&#xff0c;导致master节点无法操作node节点中的pod&#xff08;这里的不能操作&#xff0c;指定是无法查看node节点中pod的日志、启动描述、无法进入pod内部&#xff0c;即 kubec…

游戏开发引擎Cocos Creator和Unity如何对接广告-AdSet聚合广告平台

在游戏开发方面&#xff0c;游戏引擎的选择对开发过程和最终的产品质量有着重大的影响&#xff0c;Unity和Cocos是目前全球两大商用、通用交互内容开发工具&#xff0c;这两款引擎受到广泛关注&#xff0c;本文将从多个维度对两者进行比较&#xff0c;为开发者提供正确的选择建…

SEO工具-免费功能最全的5款SEO工具

随着互联网的蓬勃发展&#xff0c;搜索引擎优化&#xff08;SEO&#xff09;已经成为许多企业和个人网站必备的关键技能。然而&#xff0c;对于初学者或者运营小型网站的人来说&#xff0c;使用专业的SEO工具可能涉及较高的成本。在这篇文章中&#xff0c;我们将向您推荐五款高…

U4_2:图论之MST/Prim/Kruskal

文章目录 一、最小生成树-MST生成MST策略一些定义 思路彩蛋 二、普里姆算法&#xff08;Prim算法&#xff09;思路算法流程数据存储分析 伪代码时间复杂度分析 三、克鲁斯卡尔算法&#xff08;Kruskal算法&#xff09;分析算法流程并查集-Find-set 伪代码时间复杂度分析 一、最…

OpenWrt Lan口上网设置

LAN口上网设置 连接上openwrt&#xff0c;我用的 倍控N5105&#xff0c;eth0&#xff0c;看到Openwrt的IP是10.0.0.1 在 网络 -> 网口配置 -> 设置好 WAN 口和 LAN 口 初次使用经常重置 openwrt 所以我设置的是 静态IP模式 - 网络 -> 防火墙 -> 常规设置 ->…

IDEA2023版本创建Sping项目只能勾选17和21,却无法使用Java8?(已解决)

方案&#xff1a;替换创建项目的源 我们只知道IDEA页面创建Spring项目&#xff0c;其实是访问spring initializr去创建项目。故我们可以通过阿里云国服去间接创建Spring项目。将https://start.spring.io/或者http://start.springboot.io/替换为 https://start.aliyun.com/

什么是量子优势?

量子优势是量子计算领域正在积极努力的里程碑&#xff0c;量子计算机可以解决最强大的非量子或经典计算机无法解决的问题。 量子是指原子和分子的尺度&#xff0c;在这个尺度上&#xff0c;我们所经历的物理定律被打破&#xff0c;并且应用了一组不同的、违反直觉的定律。量子…

Linux——vim编辑文件时——.swp文件解决方案

test.cpp样例 当我们vim test.cpp进入编辑文件。 却忘记了保存退出 再次进入就会出现一下画面 当你摁下Enter键位 出现以下几个选项 O——是只读不写 E——是正常打开文件但不会载入磁盘内容 R——覆盖——是加载存储磁盘的文件(当我们忘记保存时&#xff0c;系统会自动帮我…

Django二转Day02

http #1 http 是什么#2 http特点#3 请求协议详情 -请求首行---》请求方式&#xff0c;请求地址&#xff0c;请求协议版本 -请求头---》key:value形式 -referer&#xff1a;上一次访问的地址 -user-agenet&#xff1a;客户端类型 -name&#x…

百度人工智能培训第一天笔记

参加了百度人工智能初步培训&#xff0c;主要是了解一下现在人工智能的基本情况&#xff0c;以便后续看可以参与一些啥&#xff1f; 下面就有关培训做一些记录&#xff0c;以便后续可以继续学习。 一、理论基础部分 二、实际操作部分 主要学习的百度人工智能平台如下&#xf…

同旺科技 USB 转 RS-485 适配器 -- 隔离型(定制款)

内附链接 1、USB 转 RS-485 适配器 隔离版主要特性有&#xff1a; ● 支持USB 2.0/3.0接口&#xff0c;并兼容USB 1.1接口&#xff1b; ● 支持USB总线供电&#xff1b; ● 支持Windows系统驱动&#xff0c;包含WIN10 / WIN11 系统32 / 64位&#xff1b; ● 支持Windows …

不确定度校准和可靠性图简介

图片来源 项杰 一、说明 不确定性校准是机器学习中最容易被误解的概念之一。它可以概括为这个简单的问题&#xff1a;“鉴于上述下雨的可能性&#xff0c;您是否带伞&#xff1f;” 我们在日常生活中使用主观概率和不确定性校准的概念&#xff0c;但没有意识到它们。对于不确定…

西南科技大学(数据结构A)期末自测练习二

一、填空题(每空1分,共10分) 1、在线性表的下列运算中,不改变数据元素之间结构关系的运算是( D ) A、插入 B、删除 C、排序 D、定位 2、顺序表中第一个元素的存储地址是100,每个元素的长度为2,则第5个元素的地址是( B ) A.110 B.108 C.100 …

Debian10安装VMware Tools

一、原系统 首先我在界面按CTRLALTT和CTRLSiftT都没有反应&#xff0c;没关系&#xff0c;我有办法 系统版本 管理员用户 步骤一&#xff1a;打开VMware Tools文件 步骤二、将文件复制到自己熟悉的文件内 步骤三、命令行查看文件是否复制成功存在 步骤四、解压VMware-tools…

Apache2.4 AliasMatch导致301重定向问题?

环境&#xff1a;ubuntu18.04-desktop apache2版本&#xff1a; rootubuntu:/etc/apache2# apache2ctl -v Server version: Apache/2.4.29 (Ubuntu) Server built: 2023-03-08T17:34:33apache配置&#xff1a; DocumentRoot /var/www/html # Alias就没事 # Alias "/my…

Android : SQLite 增删改查—简单应用

示例图&#xff1a; 学生实体类 Student.java package com.example.mysqlite.dto;public class Student {public Long id;public String name;public String sex;public int age;public String clazz;public String creatDate;//头像public byte[] logoHead;Overridepublic St…

火狐挂代理访问问题Software is preventing Firefox from safely connecting to this site

1、报错 Software is preventing Firefox from safely connecting to this site2、解决步骤 火狐浏览器访问http://burp&#xff0c;右上角有下载按钮下载下来证书文件 在 Firefox 中设置证书颁发机构 (CA) 验证

计算机毕业设计php+bootstrap小区物业管理系统

意义&#xff1a;随着我国经济的发展和人们生活水平的提高&#xff0c;住宅小区已经成为人们居住的主流&#xff0c;人们生活质量提高的同时&#xff0c;对小区物业管理的要求也越来越高&#xff0c;诸如对小区的维修维护&#xff0c;甚至对各项投诉都要求小区管理者做得好&…

2023.11.28-电商平台建设03 - 大数据调优手段

1.优化手段 1.1分桶表 HIVE的分桶本质上就是MR的分区操作 建表语句: create table 表名(字段 类型,.... ) clustered by(分桶字段) [sorted by (字段 [asc | desc])] into N buckets --- 定义分桶表核心语句 row format...... 分桶的作用 1) 进行数据采样工作 1.1) …