链表

背景假设:

假如要你设计一种存储电影的程序,存储电影的信息有电影名和电影等级,要求能任意添加这种信息并且显示出存储的电影信息.

设计方案:首先第一步要考虑数据的表示方法.
这种电影信息可以用数组来表示,但是会有一些弊端:

  • 浪费空间:数组的设定需要在一开始指定数组的大小,为了确保能装下足够的电影信息将会开辟很大的空间来使用,且不能保证这些空间都能被利用到(你可能想存10000部电影,但是现在只想到了10部)
  • 不利于逻辑表达:数组结构上是连续的,但逻辑上不一定连续(比如有数组开辟了10个空间,对应的索引为0~9,但是空间里面有内容的只有0,3,4三个空间,这三个空间在逻辑上对应的是1,2,3;但是用数组却表示不出来)
  • 不利于数据的更改(这里的更改指的是添加,删除,被函数作用等操作):由于数组的空间是连续性(结构上是连续性),需要在数据(电影信息)中间插入新加入的数据(新的电影信息)会很麻烦(也就是说逻辑顺序的更改会让数组结构的更改变的很麻烦)

所以这里使用一种叫 链表 的数据结构来解决这个问题

链表的结构

链表的节点(Node)
链表的节点
说明:
节点的地址是此节点的地址;而结构体是此节点的值(通常是结构体).而且结构体里面可能含有指向同类型结构的指针.结构体的内容

在这里插入图片描述这个就是指向同类型结构的指针;在这里插入图片描述这两个表示结构体里的其他成员变量(蓝色的部分就是这个成员变量的地址)
所以 这个就是一个"完整的"节点.在这里插入图片描述
由这些节点便能实现链表,完成逻辑上的连续(结构上不一定连续,靠指针进行连接),方便人们处理数据:链表
链表需要一个单独的指针指向存储第一个结构的地址(0568),该指针称为头指针(head)(0001),最后一个节点中指向下一个节点的指针为null表明这是链表的结尾.

这样就可以构建存储电影的数据了:

//使用链表---电影数量灵活

#include<stdio.h>
#include<string.h>
#include<stdlib.h>

//定义电影名的最大长度
#define FILE_NAME_MAX 3
//定义链表结构体
struct film
{
	char Title[FILE_NAME_MAX];
	int rating;
	struct film *next;
};
//函数的声明
char * s_get(char* st, int n);//输入函数

在主函数中:

//变量的声明----1.链表结构体指针(头指针)2.链表结构体指针(结构体本身指针   结构体里面的指针)3.电影名数组
	struct film *head = NULL;
	struct film *current;
	struct film *pre;
	char movices_name_input[FILE_NAME_MAX];

注意这三个指针指向的节点并不是对应链表中的某一个节点,更像是一种标记.
比如:
head就是指向链表的首节点,你可以指向任意节点当做首节点(此节点前面的节点就会排除到链表外了)
current是"不在链表中的"节点,比如要添加节点,是对current指向的节点进行操作(初始化),再让链表中的节点指针去指向current节点,从而达到添加的目的.
pre是指向中链表中的节点(此例中是链表最后一个节点),对此节点不做"其他内容的"初始化,而是让它去指向"链表外的节点"从而达到修改链表的目的.

可以将链表的使用分为三步:

1. 链表(节点)的构建

(1)请求分配新节点的内存空间

//--创建链表结构体空间
		current = (struct film*)malloc(sizeof(struct film));

(2)检测链表中是否有节点,没有的话新节点就是链表的头指针,有的话新节点就连接到当前链表中最后面节点的后面

//--如果头指针为空   就给头指针分配空间----头指针是第一个结构
		if (head == NULL)
		{
			head = current;
		}
		else
		{
			//---如果头指针有指向的东西  这个空间就给当前指针  
			pre->next = current;
		}

在这里插入图片描述创建了一个新的节点,但里面的结构体并没有内容.

2.节点的内容初始化
上一步只是分配新节点的空间(获取了新节点的地址)和新节点的位置(是头指针还是普通节点)


		//将链表结构体里面的指针初始化为空
		current->next = NULL;
		//将输入的电影名  付给结构体里面的数组
		strcpy(current->Title, movices_name_input);

		//电影评级
		puts("请输入这个电影的等级:");
		scanf("%d", &current->rating);
		while (getchar() != '\n')
		{
			continue;
		}

在这里插入图片描述将结构体的成员变量进行初始化了.

3. 将新节点放入列表中

pre = current;

这个和第1步的"新节点的位置"并没有冲突,第一步是让新节点的位置放在列表的最后面(列表已经有头指针的话),但是并不包含在链表内,这一步的操作是让新节点成为链表的一部分.
用pre指向current从而达到让pre永远指向链表最后一个节点
上面是链表的构建,这里是链表内容的显示

//显示电影列表
	//---判断头指针是否为空---为空的话 就属于没有输入电影
	if (head == NULL)
	{
		puts("一个电影都没有,憨憨");	
	}
	else
	{
		puts("电影列表是这样的:");
		//用当前指针显示信息
		//---第一个 头指针赋值
		current = head;
		while (current != NULL)
		{
			//---只要后面还有值----显示当前结构体里的值  把指针交给下一代
			printf("电影名是:%s    电影等级是:%d\n", current->Title, current->rating);
			current = current->next;
		}
	}

同样是通过操控"不在链表中"的节点来完成.
在这里插入图片描述
这里会使人感到困惑~:明明current,head是指针,为什么又说他们是"节点"?这里current"节点"是指current指向的地址中显示的内容,这个内容是节点(结构体)的内容.简单的说就是让current指针指向current的"下一个节点",从而将这个节点变成current"节点",来达到向后循环的目的.

最后就是链表(节点)的释放

//释放内存
	//从头开始释放内存
	current = head;

	//--内容没完  就1.释放内存  2.把下一个地址给头指针  
	while (current!=NULL)
	{

		head = current->next;
		free(current);
		current = head;
	}

	puts("88");

	return 0;
}

同显示链表的步骤有点像(显示链表就是用显示函数来作用节点来完成),同样这里也是操作current,但是有不一样,因为释放节点(准确的表达是释放节点的地址)会清空节点里面所有的内容,包括指向下一节点的指针(这里取个代号叫"宝贝"),这样失去了连续性,所以在这之前需要用一个东西(这里用了head来实现)现将要清掉的节点的"宝贝"保存下来,然后释放这个节点(current),再将current(要清除的节点)指向"宝贝",这样实现连续性.
不会做视频,这个地方是有先后顺序的

总结:一句话总结就是 链表就是一个个的节点连接起来的一种数据结构,对链表的操作是通过三根指针来完成.
这个程序的主要目的是为了更明白链表的操作与含义,本身的话还有很多瑕疵,比如其实要检测请求内存是否成功,要隐藏这些操作数据的细节等等.

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bussyman

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值