【王道数据结构笔记】单链表的操作--按位序插入(带头节点 代码分析)

在这里插入图片描述

🎈个人主页:豌豆射手^
🎉欢迎 👍点赞✍评论⭐收藏
🤗收录专栏:数据结构
🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步!

【王道数据结构笔记】单链表的操作--插入(代码分析)

在这里插入图片描述

引言

一 代码

bool ListInsert(LinkList& L, int i, int e)
{
	if (i < 1)
		return false;
	LNode *p;
	int j = 0;
	p = L;
	while (p != NULL && j < i - 1)
	{
		p = p->next;
		j++;
	}
	if (p == NULL)//i值不合法
		return false;
	LNode* s = (LNode*)malloc(sizeof(LNode));
	s->data = e;
	s->next = p->next;
	p->next = s;
	return true;

}

二 代码分析

以下是对ListInsert函数的每个步骤的详细分析,以及每个步骤对应的代码:

步骤一:检查插入位置是否合法
分析:首先,函数检查传入的插入位置i是否小于1,因为链表的索引通常从1开始。如果i小于1,则插入位置不合法,函数直接返回false

代码:

if (i < 1)
    return false;

步骤二:初始化指针和计数器
分析:然后,函数初始化一个指向链表节点的指针p,以及一个计数器j,用于跟踪当前遍历到的位置。p初始化为链表的头节点Lj初始化为0。

代码:

LNode *p;
int j = 0;
p = L;

在单链表的插入操作中,初始化一个指向链表节点的指针p和一个计数器j是非常必要的,它们的作用如下:

指向链表节点的指针p的作用:

  1. 遍历链表p作为遍历链表的游标,通过不断地更新p的值(即p = p->next),我们可以依次访问链表中的每一个节点,直到找到插入位置的前驱节点。

  2. 定位插入位置:一旦找到插入位置的前驱节点,我们就可以利用p来执行插入操作。具体来说,我们将新节点的next指针指向p的下一个节点(即p->next),然后将pnext指针指向新节点,完成插入。

  3. 处理边界情况:通过检查p是否为NULL,我们可以处理插入位置超出链表长度的情况。如果pNULL,则说明链表遍历结束且没有找到合法的插入位置,此时应该返回错误或者做其他相应的处理。

计数器j的作用:

  1. 跟踪遍历位置j用于跟踪当前遍历到的链表位置。在遍历链表时,每遍历一个节点,j就增加1,这样我们就可以知道当前p指向的是链表的第几个节点。

  2. 控制遍历长度:通过比较j和插入位置i,我们可以控制遍历的长度。一旦j达到i-1(即找到插入位置的前驱节点),我们就可以停止遍历并执行插入操作。这有助于避免不必要的遍历,提高代码的效率。

  3. 处理非法插入位置:如果j在遍历过程中超过了i-1但链表还未遍历完(即p不为NULL),则说明插入位置i不合法(可能小于1或者大于链表长度加1),此时应该返回错误或做其他相应的处理。

初始化p为链表的头节点L,以及j为0,是为了从链表的起始位置开始遍历,并正确跟踪当前遍历到的位置。这样,在后续的遍历和插入操作中,我们可以根据pj的值来确定插入的位置,并执行相应的操作。

步骤三:遍历链表找到插入位置的前一个节点
分析:接下来,函数进入一个while循环,用于遍历链表直到找到第i-1个节点。在循环中,p指针不断移动到下一个节点,j计数器递增。如果链表遍历结束或j达到i-1,循环结束。

代码:

while (p != NULL && j < i - 1)
{
    p = p->next;
    j++;
}

该段代码是单链表插入操作中遍历链表以找到插入位置前驱节点的关键部分

接下来,我会详细分析这段代码。

首先,while循环的条件是p != NULL && j < i - 1。这意味着循环会继续执行,直到满足以下任一条件:

  1. p变为NULL,即链表遍历结束,没有更多节点可以访问。
  2. j达到i - 1,即已经遍历到第i-1个节点,这是我们要插入新节点位置的前驱节点。

在循环体内,执行以下两个操作:

  1. p = p->next;:这行代码p指针移动到下一个节点p->next当前p指针所指向节点的下一个节点的地址,因此通过这行代码,p会指向链表中的下一个节点。

  2. j++;:这行代码将计数器j的值增加1。由于j在循环开始时初始化为0,它现在表示当前已经遍历过的节点数量。每次循环,j递增,直到达到i - 1

循环结束的条件有两种可能性:

  • 如果链表长度小于i - 1,即链表没有足够多的节点来支持在第i个位置插入新节点,那么p会先变为NULL,循环结束。

在这种情况下,插入操作不能在第i个位置进行,因为该位置超出了链表的当前长度

  • 如果链表长度大于或等于i - 1,循环将一直执行到j达到i - 1。此时,p将指向第i-1个节点,即我们要插入新节点位置的前驱节点。

一旦循环结束,p将指向插入位置的前驱节点(如果i是合法的),或者为NULL(如果i超出了链表的长度)。

接下来的操作将基于p的当前值来执行,如果p不为NULL,则执行插入操作;如果为NULL,则可能需要返回错误或进行其他适当的处理

这段代码的作用是确保在插入新节点之前,我们能够准确定位到正确的插入位置,并且为接下来的插入操作做好准备。

通过维护p指针和j计数器,代码能够以一种高效且准确的方式遍历链表,直到找到所需的位置。

步骤四:检查是否找到合法插入位置

分析:如果p变为NULL,说明链表长度小于i,即插入位置超出了链表长度,因此返回false

代码:

if (p == NULL)//i值不合法
    return false;

为什么p变为NULL,即代表链表遍历结束,没有更多节点可以访问呢?

在单链表中,每个节点都包含一个指向下一个节点的指针。当这个指针为NULL时,它表示当前节点是链表的最后一个节点,并且没有更多的节点跟随其后

因此,当我们在遍历单链表时,如果遇到p(即当前遍历到的节点指针)变为NULL,这意味着我们已经到达了链表的末尾,没有更多的节点可以访问了。

在单链表插入操作的上下文中,我们遍历链表是为了找到插入位置的前驱节点。当我们说“链表遍历结束”时,我们是指我们已经检查了链表中的每一个节点,直到找到i-1个节点(如果它存在的话),或者直到到达链表的末尾(即没有第i-1个节点,因为链表长度小于i)。

在单链表的实现中,链表的末尾是通过将最后一个节点的next指针设置为NULL来标识的。因此,当我们的指针p在遍历过程中遇到NULL时,我们可以确定已经到达了链表的末尾,没有更多的节点可供检查或访问。

简而言之,p变为NULL表示我们已经到达了链表的物理或逻辑终点,即链表的末尾,此时无法继续向后遍历,因为不存在更多的节点。

步骤五:创建新节点并插入到链表中
分析:如果找到了插入位置的前一个节点,函数将分配一个新的节点s,并设置其数据域为e。然后,将新节点的next指针指向p节点的下一个节点,最后将p节点的next指针指向新节点s,完成插入操作。

代码:

LNode* s = (LNode*)malloc(sizeof(LNode));
s->data = e;
s->next = p->next;
p->next = s;

首先,在单链表插入操作中,一旦我们找到插入位置的前一个节点(通常称为前驱节点),就可以开始执行插入操作了。

插入操作主要包括三个步骤:创建新节点、调整链接关系、更新前驱节点的next指针。

代码段中的四个操作就是按照上述步骤执行的:

  1. LNode* s = (LNode*)malloc(sizeof(LNode));
    这一行代码负责分配一个新的节点,并返回指向该节点的指针smalloc函数用于在堆上动态分配内存,sizeof(LNode)计算了LNode结构体的大小,以确保分配足够的内存空间来存储新节点。LNode*是一个指向LNode类型的指针,用于存储新节点的地址。

  2. s->data = e;
    这里,我们将新节点的数据域设置为ee是插入操作的一个参数,它表示要插入到新节点中的数据。s->data是访问新节点s中数据域的方式。

  3. s->next = p->next;
    这行代码将新节点的next指针指向p节点的下一个节点。在插入操作中,我们想要新节点成为p节点的下一个节点,所以我们需要将新节点的next指针指向p原本指向的下一个节点。

  4. p->next = s;
    最后,这行代码将p节点的next指针指向新节点s。这一步是插入操作的关键,它使得新节点s成为链表中的一部分,并且被正确地插入到p节点之后。

这四行代码组合在一起,就完成了单链表中的插入操作。新节点s现在被插入到链表中的p节点之后,其数据域被设置为e,并且链表的其余部分保持不变。注意,这里假设p不为NULL,也就是说,我们已经找到了插入位置的前驱节点。如果pNULL,则意味着插入位置超出了链表的当前长度,这时应该返回错误或进行其他相应的处理。

还需要注意的是,在实际编程中,我们通常会检查malloc是否成功分配了内存,因为如果内存分配失败,malloc会返回NULL。如果不对此进行检查,尝试访问NULL指针会导致程序崩溃。因此,在实际代码中,你可能会看到类似if (s == NULL) { /* handle error */ }的检查。此外,当不再需要链表节点时,应该使用free函数释放分配给它们的内存,以避免内存泄漏。

步骤六:返回插入成功标志
分析:最后,函数返回true,表示插入操作成功。

代码:

return true;

整个函数通过上述步骤,实现了在链表的指定位置插入一个新节点的功能。

总结

这篇文章到这里就结束了

谢谢大家的阅读!

如果觉得这篇博客对你有用的话,别忘记三连哦。

我是豌豆射手^,让我们我们下次再见

在这里插入图片描述

在这里插入图片描述

  • 15
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
链表是一种常见的数据结构,它由一个或多个节点组成,每个节点包含一个数据域和一个指针域,指针域指向下一个节点。 实现链表的查找位算法函数需要以下步骤: 1. 定义一个指针p指向链表的头节点,并定义一个变量count,用于计数。 2. 从头节点开始,遍历链表,当p指向某个节点时,计数器count加1。 3. 如果p指向的节点的数据与目标数据相等,则返回当前的计数器count,即为目标数据的位。 4. 如果p指向的节点不是目标数据,则将p指向下一个节点,重复步骤3。 5. 如果遍历完链表后仍未找到目标数据,则返回-1,表示未找到。 下面是C语言实现链表查找位算法函数的代码示例: ```c #include <stdio.h> #include <stdlib.h> // 定义链表节点结构 typedef struct Node { int data; // 数据域 struct Node* next; // 指针域 } Node; // 查找位的算法函数 int findPosition(Node* head, int target) { Node* p = head; // 指向头节点 int count = 0; // 计数器初始化为0 while (p != NULL) { count++; // 计数器加1 if (p->data == target) { return count; // 找到目标数据,返回当前计数器的值 } p = p->next; // 指向下一个节点 } return -1; // 遍历完链表未找到目标数据,返回-1 } int main() { // 创建链表 Node* head = (Node*)malloc(sizeof(Node)); head->data = 1; // 头节点数据为1 Node* node1 = (Node*)malloc(sizeof(Node)); node1->data = 2; Node* node2 = (Node*)malloc(sizeof(Node)); node2->data = 3; head->next = node1; node1->next = node2; node2->next = NULL; // 查找位示例 int target = 3; // 目标数据为3 int position = findPosition(head, target); if (position != -1) { printf("目标数据 %d 的位为 %d\n", target, position); } else { printf("未找到目标数据 %d\n", target); } // 释放链表内存 free(node2); free(node1); free(head); return 0; } ``` 在上述代码中,我们首先定义了一个指向头节点的指针p和一个计数器count,然后使用while循环遍历链表。当p指向某个节点时,计数器加1,并判断该节点的数据是否与目标数据相等。如果找到了目标数据,则返回当前计数器的值,即为目标数据的位。如果遍历完链表仍未找到目标数据,则返回-1表示未找到。最后在主函数中演示了调用该算法函数的示例。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值