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

news2024/11/23 15:22:27

前言:

        ⭐️此篇博文主要分享博主在学习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 100

typedef 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 100

typedef 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 100

typedef 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;

	//更新size
	p->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;

		//更新size
		p->size++;
	}
	
	else if (p->size <= pos)//这种p->size等不等于0都一样
	{
		if (p->size == 0 && p->size == pos)//这种特殊情况就尾插
		{
			PushBack(p, x);
		}
		else
			printf("无效插入!\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++;
		}
	}
	else
		printf("无效插入\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];
		}

		//更新size
		p->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;

		//更新size
		p->size++;
	}
	
	else if (p->size <= pos)//这种p->size等不等于0都一样
	{
		if (p->size == 0 && p->size == pos)//这种特殊情况就尾插
		{
			PushBack(p, x);
		}
		else
			printf("无效插入!\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++;
		}
	}
	else
		printf("无效插入\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];
		}

		//更新size
		p->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);//为空的时候删删试试会不会出bug
	PrintSeqList(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 == size
	ModifySeqList(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.coloradmin.cn/o/1265905.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

电脑资料删除后如何恢复?3个简单方法轻松恢复文件!

“我平常喜欢在电脑上保存很多学习资料&#xff0c;但由于资料太多&#xff0c;在清理电脑时我可能会误删一些比较有用的资料。想问问大家电脑资料删除后还有机会恢复吗&#xff1f;应该怎么操作呢&#xff1f;” 在数字化时代&#xff0c;很多用户都会选择将重要的文件直接保存…

【算法刷题】Day8

文章目录 202. 快乐数解法&#xff1a; 11. 盛最多水的容器解法&#xff1a; 202. 快乐数 原题链接 拿到题&#xff0c;我们先看题干 把一个整数替换为每个位置上的数字平方和&#xff0c;有两种情况&#xff1a; 重复这个过程始终不到 1&#xff08;无限死循环&#xff09;结…

【计算机网络学习之路】序列化,反序列化和初识协议

文章目录 前言一. 序列化和反序列化1.自己实现2. JSON 二. 初识协议结束语 前言 本系列文章是计算机网络学习的笔记&#xff0c;欢迎大佬们阅读&#xff0c;纠错&#xff0c;分享相关知识。希望可以与你共同进步。 本篇博文讲解应用层的序列化和反序列化&#xff0c;还有见一…

笔记:Pika Labs 3D 动画生成工具

Pika Labs 一款3D 动画生成工具 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/134657306 目 录 1. 简介2. 准备2.1 安装 discord2.2 加入 Discord 频道 3. Pika 使用指南2.1 快速开始2.2 从图像到视频2.3 Pika Bot按钮2.4 提示&#xff08;Prompt&a…

【代码】数据驱动的多离散场景电热综合能源系统分布鲁棒优化算法matlab/yalmip+cplex/gurobi

程序名称&#xff1a;数据驱动的多离散场景电热综合能源系统分布鲁棒优化算法 实现平台&#xff1a;matlab-yalmip-cplex/gurobi 代码简介&#xff1a;数据驱动的分布鲁棒优化算法。考虑四个离散场景&#xff0c;模型采用列与约束生成(CCG)算法进行迭代求解&#xff0c;场景分…

选择更灵活的设计工具:SOLIDWORKS 软件网络版与单机版的比较

随着科技的飞速发展&#xff0c;工程设计领域对于高效、灵活的设计工具需求日益增加。SOLIDWORKS 作为一款广受欢迎的三维设计软件&#xff0c;提供了网络版和单机版两种选择。在本文中&#xff0c;我们将深入探讨这两个版本的区别&#xff0c;并为您详细介绍它们的价格差异。 …

前端面试灵魂提问

1.自我介绍 2.在实习中&#xff0c;你负责那一模块 3.any与unknow的异同 相同点&#xff1a;any和unkonwn 可以接受任何值 不同点&#xff1a;any会丢掉类型限制&#xff0c;可以用any 类型的变量随意做任何事情。unknown 变量会强制执行类型检查&#xff0c;所以在使用一个…

openwrt配置SSL证书实现https加密访问

前言&#xff1a;目前来看这个用处不是很大&#xff0c;因为只能访问一个端口&#xff0c;且因为80和443都已经被运营商封了&#xff0c;所以访问时还是得带端口。以下以阿里云证书为例&#xff1a; 一、申请证书 这个很简单&#xff0c;不想去截图了&#xff0c;直接去申请你…

【设计模式-2.2】创建型——简单工厂和工厂模式

说明&#xff1a;本文介绍设计模式中&#xff0c;创建型设计模式中的工厂模式&#xff1b; 飞机大战 创建型设计模式&#xff0c;关注于对象的创建&#xff0c;本文介绍的简单工厂和工厂模式同样也是。举一个游戏例子&#xff0c;如飞机大战游戏中&#xff0c;屏幕中敌人类型…

android viewpager 禁止滑动

android viewpager 禁止滑动 前言一、viewpager 禁止滑动是什么&#xff0c;有现成方法吗&#xff1f;二、使用setOnTouchListener三、使用自定义viewpager总结 前言 本文介绍了本人有一个相关的需求需要实现这一功能&#xff0c;在过程中发现自己之前没做过&#xff0c;然后记…

【带头学C++】----- 八、C++面向对象编程 ---- 8.5 struct结构体类型增强使用说明

目录 8.5 struct结构体类型增强使用说明 8.5.1 C结构体可以定义成员函数 8.5.2 c中定义结构体变量可以不加struct关键字 8.6 bool布尔类型关键字 8.5 struct结构体类型增强使用说明 第六章对结构体的使用、内存对齐以及数组、深拷贝和浅拷贝进行了一个详细的说明&#xff0c…

统信UOS_麒麟KYLINOS上使用远程SSH连接的工具electerm

原文链接&#xff1a;统信UOS/麒麟KYLINOS上使用SSH工具electerm Hello&#xff0c;大家好啊&#xff01;在我们日常的工作和学习中&#xff0c;远程控制和管理服务器已经成为一项常见且必要的技能。尤其是对于IT专业人士和开发者来说&#xff0c;一个高效、稳定的远程SSH连接工…

智能AI系统ChatGPT网站系统源码+Midjourney绘画+支持DALL-E3文生图,支持最新GPT-4-Turbo模型

一、AI创作系统 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;可以说SparkAi是目前国内一款的ChatGPT对接OpenAI软件系统。那么如何搭建部署AI…

漏洞扫描-德迅云安全漏洞扫描服务

漏洞扫描是指基于漏洞数据库&#xff0c;通过扫描等手段对指定的远程或者本地计算机系统的安全脆弱性进行检测&#xff0c;发现可利用漏洞的一种安全检测的行为。 漏洞扫描的主要目的是发现系统、网络或应用程序中可能存在的安全漏洞和缺陷&#xff0c;以便及时修复这些漏洞和缺…

基于STM32单片机的智能家居系统设计(论文+源码)

1.系统设计 基于STM32单片机的智能家居系统设计与实现的具体任务&#xff1a; &#xff08;1&#xff09;可以实现风扇、窗帘、空调、灯光的开关控制&#xff1b; &#xff08;2&#xff09;具有语音识别功能&#xff0c;可以通过语音控制家电&#xff1b; &#xff08;3&a…

Pytorch:torch.utils.data.DataLoader()

如果读者正在从事深度学习的项目&#xff0c;通常大部分时间都花在了处理数据上&#xff0c;而不是神经网络上。因为数据就像是网络的燃料&#xff1a;它越合适&#xff0c;结果就越快、越准确&#xff01;神经网络表现不佳的主要原因之一可能是由于数据不佳或理解不足。因此&a…

【人工智能】人工智能的技术研究与安全问题的深入讨论

前言 人工智能&#xff08;Artificial Intelligence&#xff09;&#xff0c;英文缩写为AI。 它是研究、开发用于模拟、延伸和扩展人的智能的理论、方法、技术及应用系统的一门新的技术科学。人工智能是新一轮科技革命和产业变革的重要驱动力量。 &#x1f4d5;作者简介&#x…

vscode注释插件「koroFileHeader」

前言 在vscode上进行前端开发&#xff0c;有几个流行的注释插件&#xff1a; Better CommentsTodo TreekoroFileHeaderDocument ThisAuto Comment Blocks 在上面的插件中我选择 koroFileHeader 做推荐&#xff0c;原因一是使用人数比较多&#xff08;最多的是 Better Commen…

029 - STM32学习笔记 - ADC(三) 独立模式单通道DMA采集

029 - STM32学习笔记 - 单通道DMA采集&#xff08;三&#xff09; 单通道ADC采集在上节中学习完了&#xff0c;这节在上节的内容基础上&#xff0c;学习单通道DMA采集。程序代码以上节的为基础&#xff0c;需要删除NVIC配置函数、中段服务子程序、R_ADC_Mode_Config()函数中使能…

探索Python内置类属性__repr__:展示对象的魅力与实用性

概要 在Python中&#xff0c;每个对象都有一个内置的__repr__属性&#xff0c;它提供了对象的字符串表示形式。这个特殊的属性在调试、日志记录和交互式会话等场景中非常有用。本文将详细介绍__repr__属性的使用教程&#xff0c;包括定义、常见应用场景和注意事项&#xff0c;…