单链表的简单介绍


前言

在牛客网刷C语言的题的时候,有这么一道题,题目描述:先输入n个整数,按照数据输入的顺序建立一个带头结点的单链表,再输入一个数据m,将单链表中的值为m的结点全部删除。输出删除后的单链表信息及元素个数。
输入描述:
包括三行:
第一行输入数据个数n (3≤n≤100);
第二行依次输入n个整数,用空格分隔;
第三行输入欲删除数据m。

输出描述:
包括两行:
第一行输出完成删除后的单链表长度;
第二行依次输出完成删除后的单链表数据。


一、链表是什么?

(1)链表是一种数据结构,数据结构是描述数据在内存中的存储方式。我们经常遇到的数组,一般叫做顺序表,在内存中有序存储。还有一种叫做链表,如下图
在这里插入图片描述
(2)如何创建一个结点
一起来分析下,结点包含数据和指针,显然,使用结构体来创建结点更为合适

struct Node
{
	int data ; // 数据域
	struct Node * next; //指针域
};

二、创建链表

再来回顾一下题目:
先输入n个整数,按照数据输入的顺序建立一个带头结点的单链表,再输入一个数据m,将单链表中的值为m的结点全部删除。输出删除后的单链表信息。
(1)搭建整体框架

struct Node
{
	int data;
	struct Node * next;
};
#include<stdio.h>
int main()
{
	struct Node * list = NULL; //让list指针来管理这个链表,让list指针指向链表的第一个元素。
	int n = 0; 
	scanf("%d",&n);
	int d = 0; 
	int i = 0;
	for(i=0;i<n;i++)
	{
		scanf("%d",&d);//输入n个整数
		
	}
	return 0;
}

(2)分析
结点首先需要向内存申请动态内存空间,需要malloc函数。结点需要数据和指针两个内容,那么数据域很简单了,就是我们输入的d,但是指针呢?我们如何让第一个结点指向第二个结点?这时候就想到,需要一个tail指针来记录上一个结点尾部的位置,也就是上一个结点存储地址的地方,让tail指针指向第二个结点的起始位置,是不是这两个结点就链接起来了。看代码:

struct Node
{
	int data;
	struct Node * next;
};
#include<stdio.h>
#include<stdlib.h>
int main()
{
	struct Node * list = NULL; //让list指针来管理这个链表,让list指针指向链表的第一个元素。
	struct Node * tail = NULL;//尾指针
	int n = 0; 
	scanf("%d",&n);
	int d = 0; 
	int i = 0;
	for(i=0;i<n;i++)
	{
		scanf("%d",&d);//输入n个整数
		struct Node * pn = (struct Node *)malloc(sizeof(struct Node));
		pn->data = d;
		pn->next = NULL;
		//如果链表中没有值,说明list 是空指针
		if(list == NULL)
		{
			list = pn; //让list 指向pn的第一个元素
			tail = pn;
		}
		else
		{
			tail ->next = pn; //我上面说的,tail指向的next 让它指向第二个结点的第一个位置
			tail = pn;
		}
	}
	return 0;
}

链表就创建好了


三、删除指定的数字

(1)分析
当我们输入了要删除的数字,那么可能有两种情况,一种是如果删除的数字正好是第一个,第二种是如果删除的数字不止一个怎么办?首先我们需要一个指针帮助我们遍历整个链表,定义cur指针。首先解决第一个问题,如果正好是第一个,那是不是直接跳过第一个,直接让list指针指向第二个结点即可,然后释放掉第一个结点。看代码:(不接着上面的写了,最后再一起总结)

int m = 0; // 要删除的数字
scanf("%d",&m);
struct Node * cur = list ; //cur是用来遍历整个链表的
while(cur)
{
	if(cur->data == m)
	{
		struct Node * pd = cur;//pd指针的作用是指向要被释放的区域,方便及时释放内存空间
		//如果是第一个就是要删除的数字
		if(cur == list)
		{
			list = cur->next;
			cur = list;
		}
		//pd在这里还是指向第一个结点,方便释放
		else//看第二点
		{
			
		}
	}
	//如果不是就往后遍历就好了
	else
	{
		cur = cur ->next;
	}
}

(2)else里面应该怎么写?
假设我们找到的不是第一个?而是第二个及以后怎么办?以第二个为例,想跳过第二个,是不是需要第一个结点的指针直接指向第三个结点的第一个元素是不是就可以了?我们是单链表,cur都遍历到第二个结点了,如何找到第一个结点呢?这样,还需要一个指针,叫prev,作用指向cur的前一个结点,看代码

int m = 0; // 要删除的数字
scanf("%d",&m);
struct Node * cur = list ; //cur是用来遍历整个链表的
struct Node * prev = NULL;
while(cur)
{
	if(cur->data == d)
	{
		struct Node * pd = cur;//pd指针的作用是指向要被释放的区域,方便及时释放内存空间
		//如果是第一个就是要删除的数字
		if(cur == list)
		{
			list = cur->next;
			cut = list;
		}
		//pd在这里还是指向第一个结点,方便释放
		else
		{
			//让prev指向的结点的指针指向cur指向结点的下一个结点就可以了
			prev->next = cur->next;
			cur = prev->next;
		}
		n--; //每进来一次,就删除一个
		free(pd);//及时释放掉被删除的结点
	}
	//如果不是就往后遍历就好了
	else
	{
		prev = cur; //始终让prev指向cur的前一个结点
		cur = cur ->next;
	}
}



四、输出

(1)再来看看题目
输出描述:
包括两行:
第一行输出完成删除后的单链表长度;
第二行依次输出完成删除后的单链表数据。
看代码

//上文我们每删除一个数字n就减少一个,最后输出的n就是单链表的长度
printf("%d\n",n);
//输出单链表,还是用cur来遍历数组
cur = list;
while(cur)
{
	printf("%d ",cur->data);
	cur = cur->next;
}
//释放整个单链表
cur = list;
struct Node * del = NULL;//记录cur前一个结点,也就是要被释放的结点
while(cur)
{
	del = cur;
	cur = cur ->next;
	free(del);
}
list=NULL; //因为单链表已经释放完了,list作为链表指针当然需要立马置为空指针。

(2)总体代码

struct Node
{
	int data;
	struct Node * next;
};
#include<stdio.h>
#include<stdlib.h>
int main()
{
	struct Node * list = NULL; //让list指针来管理这个链表,让list指针指向链表的第一个元素。
	struct Node * tail = NULL;
	int n = 0; 
	scanf("%d",&n);
	int d = 0; 
	int i = 0;
	for(i=0;i<n;i++)
	{
		scanf("%d",&d);//输入n个整数
		struct Node * pn = (struct Node *)malloc(sizeof(struct Node));
		pn->data = d;
		pn->next = NULL;
		//如果链表中没有值,说明list 是空指针
		if(list == NULL)
		{
			list = pn; //让list 指向pn的第一个元素
			tail = pn;
		}
		else
		{
			tail ->next = pn; //我上面说的,tail指向的next 让它指向第二个结点的第一个位置
			tail = pn;
		}
	}
	int m = 0; // 要删除的数字
scanf("%d",&m);
struct Node * cur = list ; //cur是用来遍历整个链表的
struct Node * prev = NULL;
while(cur)
{
	if(cur->data == m)
	{
		struct Node * pd = cur;//pd指针的作用是指向要被释放的区域,方便及时释放内存空间
		//如果是第一个就是要删除的数字
		if(cur == list)
		{
			list = cur->next;
			cur = list;
		}
		//pd在这里还是指向第一个结点,方便释放
		else//看第二点
		{
			//让prev指向的结点的指针指向cur指向结点的下一个结点就可以了
			prev->next = cur->next;
			cur = prev->next;
		}
		n--; //每进来一次,就删除一个
		free(pd);//及时释放掉被删除的结点
	}
	//如果不是就往后遍历就好了
	else
	{
		prev = cur; //始终让prev指向cur的前一个结点
		cur = cur ->next;
	}
}
	printf("%d\n",n);
//输出单链表,还是用cur来遍历数组
	cur = list;
	while(cur)
	{
		printf("%d ",cur->data);
		cur = cur->next;
	}
//释放整个单链表
	cur = list;
	struct Node * del = NULL;//记录cur前一个结点,也就是要被释放的结点
	while(cur)
	{
		del = cur;
		cur = cur ->next;
		free(del);
	}
	list=NULL; //因为单链表已经释放完了,list作为链表指针当然需要立马置为空指针。
	return 0;
}

输出结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

总结

这篇博客对单链表的一种简单题型做了详细的描述,感谢耐心看完博客的每一位小伙伴,相信你对单链表和数据在内存中的存储方式又多了一些新的理解,一路同行,与你相伴,希望每个小伙伴如果喜欢还请点赞收藏,期待与你的下一次相遇。

  • 38
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值