单链表的创建,遍历,删除和插入(手把手教程,因为我也是新手)

引入:对比单链表和数组

单链表:

        优点:改变数据很方便(因为只用将next指向下一个的开头即可下面会有所提及)

        缺点:访问慢(因为是要逐个逐个地访问)

数组:

        优点:访问数据快(因为只需找到下标即可)

        缺点:改变数据即删除插入麻烦

准备活动:

       结构体创还能, 头结点,尾结点,链表的工作原理

         1.对结构体的声明和重命名

               

        结构体声明大体没什么变化,

        主要区别是“struct Node* next”

        这里定义了一个结构体指针用来链接个个结构体

        2.        头结点,尾结点同理

        3.       工作原理:通过指针域将结构体一一串联

1.单链表的创建

ListNode* Creat(int n)
{
	ListNode* head, * tail, * p;
	head = (ListNode*)malloc(sizeof(struct Node));//分配头结点空间
	tail = head;//意思是尾指针指向表头
	head->data = NULL;//head data空值
	head->next = NULL;//同理
	//int a = 0;
	int i = 0;
	while (i != n)//while和for都没影响,主要是main函数中的n忘了赋值
	{
		int a = 0;
		scanf("%d", &a);
		p = (ListNode*)malloc(sizeof(ListNode));//给每个分配空间,来一个分配一个
		p->data = a;//赋值
		p->next = NULL;//未赋值的令为空指针
		tail->next = p;//(第一次)指向头结点的尾指针指向下一个的data值,使其连接起来,后面以此类推
		tail = p;//设置新的表尾
		i++;
	}
	tail->next = NULL;//可有可无,前面的p->next = NULL,然后把p的赋给tail,已经完成
	return head;//返回一个结构体指针
}

1).定义结构体指针 :头结点head,尾结点tail,和p

2)头结点处理:给头结点分配空间,用malloc函数,此时注意将尾结点tail指向head,并把head的data和next令为空指针, (因为head指针不用来赋值,赋值用p)此后head就可以不动了,

3)对p的赋值:接着用for或者是while循环对结构体赋值,注意的是,每次在赋值之前,要给p重新分配空间,这样才可以使用,将数据域赋值以后,将指针域赋值为NULL,

4)对tail的处理:此时的tail指向的是head,把tail的指针域next指向p的数据域,即是将此时head的next的指针域的地址赋值以p的数据域,然后把p赋值给tail,即将tail令为新的表尾

(重点:如果要想好好理解,则应该把tail->next理解为head的指针域,因为此时tail等同于head)

5)返回head操作:其实tail->next= NULL

可以忽略不计,因为,将p赋给tail前,p的指针域已经为NULL

okok,这下我们就先完成了链表的创建喽

下一站:链表的遍历

2.单链表的遍历

void ListTravel(ListNode* L)//遍历链表
{
	ListNode* p = L->next;
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

遍历的话,就相较于其他的会加单一点

1)首先,要得到我们创建的链表的头结点,在main函数中建一个即可

2)其次,我们要新建一个结构体指针,命名啥都可以,为方便还是名为p吧

加入while循环,只要p!=NULL的话,就打印即可,然后p指向p->next,进入下一个节点

3)注意的事,要打印\n,因为每做完一次,都要打印一遍

遍历就完啦,简单吧

下一站:删除

3.单链表的删除

void DelectNode(ListNode* head, int index)//删除链表,要传目标的index,思路,找到目标前一个的next,把目标free掉,再把前一个的next和目标后一个的连接起来,同时要判断是否会出界之类的
{
	int i = 1;
	ListNode* p = head;
	while (p->next != NULL && i < index)
	{
		p = p->next;
		i++;
	}
	if (p->next == NULL || i > index||p == NULL)//判断无效的情况
	{
		printf("删除失败");
		return NULL;
	}
	ListNode* q = p->next;
	p->next = q->next;
	free(q);
}

1)要删除链表中的一部分,首先我们要找到要删除的结点,先定义一个结构体指针p指向head,这里我们可以使用while循环,来找到要删结点的前一个结点

比如:输入数据为1 2 3 4 5

             删除目标数据:3

           结果呈现:1 2 4 5

据分析得知,3是在第三个位置上的,所以我们要找的点为2

运用while循环,如果循环没有终止,p指向下一个节点,i++

2)while循环结束后,我们就找到了第二个节点,再删除之前要判断是否会出现越界或者其他之类的情况,否则删除失败,return NULL返回空指针,本质上使得函数终止,同时也什么也返回不了

3)删除实例,咱们定义一个新的结构体指针q,指向下一个节点即要删除的点,然后把q给free掉

,也就是删除操作,然后把p的next指向q的next,也就是把要删除节点的下一个结点的指针赋给p的指针域,(这里是指针传指针,不同于之前的地址传指针)

4)最后在main函数中使用我们的遍历就可以看到结果啦!

最后呢,我们会讲到单链表的插入

目前也就学到这里,见谅见谅

4.单链表的插入

void InsertList(ListNode*head,int index2)//插入链表,自己思考:在创建一个链表,把头结点的next和结点next结合,尾结点和后一个结合
{
	ListNode* p = head;
	int i = 0;
	while (p != NULL && i < index2 - 1)
	{
		p = p->next;
		i++;
	}
	if (p == NULL || p->next == NULL || i > index2 - 1)
	{
		printf("插入失败");
			return NULL;
	}
	ListNode* new = (ListNode*)malloc(sizeof(ListNode));
	new->data = 3;
	new->next = p->next;
	p->next = new;
}

1)跟单链表的删除一样,要想实现链表的插入,首先要先找到插入点,同理用while循环即可

2)伴随的也还是要判断是否越界等问题,同理上述

3)比如说

在删除链表后,原表表现为:1 2 4 5

那我们想插入3的话

就恢复成1 2 3 4 5

4)首先,建立一个新的结构体指针new,并为其分配好空间,赋值后

然后我们就可以开始操作了,首先我们把new的指针域赋值以p的指针域,使new的指针域指向4的位置,然后再将p的指针与赋值以new,使p的指针指向new的data位置,通俗的来讲嘛就是,

p->next原来指向4,用new->next取代后,new->next指向4,然后呢,p->next也不可以在指向4,所以将矛头调转,指向new的数据域,也就是3

下面引用B站上一位up的图片(kkghcty),方便大家理解

5.总代码,软件为VS2022,所以会有#define _CRT_SECURE_NO_WARNINGS这个

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct Node {
	int data;
	struct Node* next;
}ListNode;

ListNode* Creat(int n);//创建来链表

void ListTravel(ListNode* L);//遍历链表

void DelectNode(ListNode* head, int index);//删除链表

void InsertList(ListNode*head, int index2);//插入链表
int main()
{
	int n = 0;
	scanf("%d", &n);
	ListNode* head = Creat(n);//创建链表

	ListTravel(head);//遍历链表

	int index1 = 3;//表示删除结点为第三个
	DelectNode(head,3);//删除链表
	ListTravel(head);//遍历链表打印

	int index2 = 3;//表示插入结点为第三个
	InsertList(head, index2);
	ListTravel(head);//遍历链表打印
}


ListNode* Creat(int n)
{
	ListNode* head, * tail, * p;
	head = (ListNode*)malloc(sizeof(struct Node));//分配头结点空间
	tail = head;//意思是尾指针指向表头
	head->data = NULL;//head data空值
	head->next = NULL;//同理
	//int a = 0;
	int i = 0;
	while (i != n)//while和for都没影响,主要是main函数中的n忘了赋值
	{
		int a = 0;
		scanf("%d", &a);
		p = (ListNode*)malloc(sizeof(ListNode));//给每个分配空间,来一个分配一个
		p->data = a;//赋值
		p->next = NULL;//未赋值的令为空指针
		tail->next = p;//(第一次)指向头结点的尾指针指向下一个的data值,使其连接起来,后面以此类推
		tail = p;//设置新的表尾
		i++;
	}
	tail->next = NULL;//可有可无,前面的p->next = NULL,然后把p的赋给tail,已经完成
	return head;//返回一个结构体指针
}
void ListTravel(ListNode* L)//遍历链表
{
	ListNode* p = L->next;
	while (p != NULL)
	{
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}
void DelectNode(ListNode* head, int index)//删除链表,要传目标的index,思路,找到目标前一个的next,把目标free掉,再把前一个的next和目标后一个的连接起来,同时要判断是否会出界之类的
{
	int i = 1;
	ListNode* p = head;
	while (p->next != NULL && i < index)
	{
		p = p->next;
		i++;
	}
	if (p->next == NULL || i > index||p == NULL)//判断无效的情况
	{
		printf("删除失败");
		return NULL;
	}
	ListNode* q = p->next;
	p->next = q->next;
	free(q);
}
void InsertList(ListNode*head,int index2)//插入链表,自己思考:在创建一个链表,把头结点的next和结点next结合,尾结点和后一个结合
{
	ListNode* p = head;
	int i = 0;
	while (p != NULL && i < index2 - 1)
	{
		p = p->next;
		i++;
	}
	if (p == NULL || p->next == NULL || i > index2 - 1)
	{
		printf("插入失败");
			return NULL;
	}
	ListNode* new = (ListNode*)malloc(sizeof(ListNode));
	new->data = 3;
	new->next = p->next;
	p->next = new;
}

目前学习就到这里,感谢大家观看!

求花花!

  • 35
    点赞
  • 37
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值