鱼C论坛

 找回密码
 立即注册
查看: 1292|回复: 19

[已解决]单链表尾插入和头插入

[复制链接]
发表于 2023-4-10 21:20:44 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
单链表尾插入和头插入函数代码实现是怎样的呢?
虽然有注释 但是还是看不太明白逻辑 能以生动形象的方式解答以下吗?
或者图片的方式给我以下分析思路!万分感谢!
#include<stdio.h>
#include<stdlib.h> 

struct Node
{
    int value; // 节点存储的值
    struct Node *next; // 指向下一个节点的指针
}; 

// 判断单链表是否为回文链表
int recurCheck(struct Node *head, struct Node *tail);

// 遍历并打印单链表
void printNode(struct Node *head);

// 在链表头部插入节点
void insertNode_one(struct Node **head, int value);

// 在链表尾部插入节点
void insertNode_two(struct Node **head, int value);


void insertNode_one(struct Node **head, int value)
{
    struct Node *node; // 定义节点指针
    struct Node *temp; // 临时变量指针,用于保存当前头结点的下一个节点
 
    node = (struct Node *)malloc(sizeof(struct Node)); // 为节点分配内存空间
    if(node == NULL) // 判断内存分配是否成功
    {
        printf("内存分配失败了!\n");
        exit(1);    
    }
    
    node->value = value; // 将输入的值存入新节点value成员变量中
    
    if(*head != NULL) // 单链表不为空
    {
        temp = *head; // 将临时指针变量保存为当前头结点的下一个节点
        *head = node; // 将node指针变量的值更新为头结点指针变量的值
        node->next = temp; // 将node指针变量的下一个节点为当前头结点的下一个节点
    }
    else // 单链表为空
    {
        *head = node; // 直接将node指针变量赋值给头结点指针变量
        node->next = NULL; // 将node指针变量的下一个节点赋值为NULL
    }

}

void insertNode_two(struct Node **head, int value)
{
    struct Node *node; // 定义节点指针
    struct Node *temp; // 临时变量指针,用于保存当前头结点的下一个节点
    
    node = (struct Node *)malloc(sizeof(struct Node)); // 为节点分配内存空间
    if(node == NULL) // 判断内存分配是否成功
    {
        printf("内存分配失败了!\n");
        exit(1);    
    }
    
    node->value = value; // 将输入的值存入新节点value成员变量中
    
    if(*head != NULL) // 单链表不为空
    {
        temp = *head;
        
        while(temp->next != NULL) // 寻找最后一个节点
        {
            temp = temp->next;
        }
        
        temp->next = node; // 最后一个节点的下一个节点为新插入的节点
        node->next = NULL; // 新插入的节点的下一个节点为NULL
    } 
    else // 单链表为空
    {
        *head = node;
        node->next = NULL; 
    }
}

void printNode(struct Node *head)
{
    struct Node *current; // 当前节点指针变量
    
    current = head; // 将head指针变量赋值给current指针变量
    printf("录入的单链表是:"); 
    while(current != NULL) // 循环打印链表中的所有节点
    {
        printf("%d ",current->value);
        current = current->next;

    }
    putchar('\n');

}

int recurCheck(struct Node *head_one, struct Node *head_two)
{
    // 循环遍历两个链表中的每个节点,并比较节点值是否相等
    while(head_one)
    {
        if(head_one->value != head_two->value)
        {
            return 0;
        }
        head_one = head_one->next; 
        head_two = head_two->next;
    }
    
    return 1;
}

int main(void)
{
    struct Node *head_one = NULL; // 链表1的头结点指针变量
    struct Node *head_two = NULL; // 链表2的头结点指针变量
    
    int input; // 用于存储用户输入的数字
    
    while(1)
    {
        printf("请输入一个整数(输入-1表示结束):");
        scanf("%d",&input);
        
        if(input == -1)
        {
            break;    
        }
        
        insertNode_one(&head_one, input); // 头插入链表1
        insertNode_two(&head_two, input); // 尾插入链表2

    }
    
    printNode(head_two); // 打印链表2,确认尾插入是否成功
    printNode(head_one); // 打印链表1,确认头插入是否成功
    
    if (recurCheck(head_one, head_two)) // 判断链表是否为回文链表
    {
        printf("录入的单链表是回文链表!\n");
    }
    else
    {
        printf("录入的单链表不是回文链表!\n");
    }
    
    return 0;    
}
最佳答案
2023-4-10 21:35:12
a905448839 发表于 2023-4-10 21:29
还能再详细点吗?我想在草稿纸自己代入演算以下。。

当然可以。让我们用一个详细的示例,分步骤解释头插入和尾插入的过程。

首先,我们将使用头插入法。我们将使用相同的整数序列:1、2 和 3。

1、插入 1:

  • 创建新节点 node,将值 1 赋给 node->value
  • 检查链表是否为空(*head == NULL),链表为空
  • 设置 node 为头节点:*head = node
  • 设置 node->next = NULL
    结果:链表:1


2、插入 2:

  • 创建新节点 node,将值 2 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 设置新节点为头节点:*head = node
  • 设置新头节点的下一个节点为原头节点:node->next = temp
    结果:链表:2->1


3、插入 3:

  • 创建新节点 node,将值 3 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 设置新节点为头节点:*head = node
  • 设置新头节点的下一个节点为原头节点:node->next = temp
    结果:链表:3->2->1


现在,我们使用尾插入法。我们将使用相同的整数序列:1、2 和 3。

1、插入 1:

  • 创建新节点 node,将值 1 赋给 node->value
  • 检查链表是否为空(*head == NULL),链表为空
  • 设置 node 为头节点:*head = node
  • 设置 node->next = NULL
    结果:链表:1


2、插入 2:

  • 创建新节点 node,将值 2 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 遍历链表,直到找到最后一个节点(temp->next == NULL)
  • 将新节点连接到最后一个节点:temp->next = node
  • 设置 node->next = NULL
    结果:链表:1->2


3、插入 3:

  • 创建新节点 node,将值 3 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 遍历链表,直到找到最后一个节点(temp->next == NULL)
  • 将新节点连接到最后一个节点:temp->next = node
  • 设置 node->next = NULL
    结果:链表:1->2->3


现在您可以用这个详细的示例在草稿纸上代入演算。这将帮助您更好地理解头插入和尾插入的过程。

在演算时,请注意以下关键点:

  • 对于头插入法,新节点总是插入到链表的开头。因此,当我们插入一个新节点时,原来的头节点需要变成新节点的下一个节点。这就是为什么我们需要将原头节点的指针赋给新节点的 next 指针,并将新节点设置为头节点。
  • 对于尾插入法,新节点总是插入到链表的末尾。因此,我们需要遍历链表,找到最后一个节点,然后将新节点连接到最后一个节点。最后一个节点的 next 指针需要指向新节点,新节点的 next 指针设置为 NULL。


通过对这些示例进行演算,您将更深入地了解头插入和尾插入的概念和实现。如果您在演算过程中遇到任何困惑或问题,请随时向我求助。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2023-4-10 21:22:55 | 显示全部楼层
这段代码实现了单链表的头**和尾**两个函数,下面我们来简单解释一下它们的逻辑:

头**函数 insertnode_one

1. 首先判断单链表是否为空,如果不为空,将当前链表的头结点保存在temp指针变量中;
2. 分配新节点空间,将输入的值存储在value成员变量中;
3. 将头指针变量head指向新分配的节点,更新为当前节点;
4. 将新节点的next成员变量指向之前的头节点temp。

尾**函数 insertnode_two

1. 首先判断单链表是否为空,如果不为空,遍历整个链表,找到最后一个节点;
2. 分配新节点空间,将输入的值存储在value成员变量中;
3. 将最后一个节点的next成员变量指向新分配的节点;
4. 将新节点的next成员变量设置为null。

通过这两个函数实现的单链表可以用于判断回文字符串。其中,头**函数可以快速地构造与原字符串相反的字符串(即翻转后的字符串),而尾**函数构造的则是原字符串。最后比较这两个链表是否相等即可判断回文字符串。

同时,该代码还提供了打印链表的函数printnode和判断回文链表的函数 recurcheck,可以方便地观察和调试单链表的构建过程。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-10 21:27:52 | 显示全部楼层
我将用一个简单的示例来解释头插入和尾插入的不同之处,并帮助您理解代码中的逻辑。

假设我们有一个空链表,现在我们要向链表中插入三个整数:1、2 和 3。以下是头插入和尾插入的过程及结果。

头插入:

1、插入 1
链表:1
2、插入 2
链表:2->1
3、插入 3
链表:3->2->1

尾插入:

1、插入 1
链表:1
2、插入 2
链表:1->2
3、插入 3
链表:1->2->3

现在,我们将分析这两个插入函数的代码实现。

1、头插入函数(insertNode_one):
a. 创建一个新的节点,并将输入值赋给新节点。
b. 如果链表不为空,则将当前头节点的下一个节点赋给新节点的下一个节点,然后将新节点作为新的头节点。
c. 如果链表为空,则将新节点作为头节点,新节点的下一个节点设置为 NULL。

2、尾插入函数(insertNode_two):
a. 创建一个新的节点,并将输入值赋给新节点。
b. 如果链表不为空,遍历链表直到找到最后一个节点,将新节点插入到最后一个节点的后面,新节点的下一个节点设置为 NULL。
c. 如果链表为空,将新节点作为头节点,新节点的下一个节点设置为 NULL。

通过这些解释,希望您能对头插入和尾插入有更清晰的认识。如果您还有其他问题,请随时询问。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-4-10 21:29:25 | 显示全部楼层
isdkz 发表于 2023-4-10 21:27
我将用一个简单的示例来解释头插入和尾插入的不同之处,并帮助您理解代码中的逻辑。

假设我们有一个空链 ...

还能再详细点吗?我想在草稿纸自己代入演算以下。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-10 21:35:12 | 显示全部楼层    本楼为最佳答案   
a905448839 发表于 2023-4-10 21:29
还能再详细点吗?我想在草稿纸自己代入演算以下。。

当然可以。让我们用一个详细的示例,分步骤解释头插入和尾插入的过程。

首先,我们将使用头插入法。我们将使用相同的整数序列:1、2 和 3。

1、插入 1:

  • 创建新节点 node,将值 1 赋给 node->value
  • 检查链表是否为空(*head == NULL),链表为空
  • 设置 node 为头节点:*head = node
  • 设置 node->next = NULL
    结果:链表:1


2、插入 2:

  • 创建新节点 node,将值 2 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 设置新节点为头节点:*head = node
  • 设置新头节点的下一个节点为原头节点:node->next = temp
    结果:链表:2->1


3、插入 3:

  • 创建新节点 node,将值 3 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 设置新节点为头节点:*head = node
  • 设置新头节点的下一个节点为原头节点:node->next = temp
    结果:链表:3->2->1


现在,我们使用尾插入法。我们将使用相同的整数序列:1、2 和 3。

1、插入 1:

  • 创建新节点 node,将值 1 赋给 node->value
  • 检查链表是否为空(*head == NULL),链表为空
  • 设置 node 为头节点:*head = node
  • 设置 node->next = NULL
    结果:链表:1


2、插入 2:

  • 创建新节点 node,将值 2 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 遍历链表,直到找到最后一个节点(temp->next == NULL)
  • 将新节点连接到最后一个节点:temp->next = node
  • 设置 node->next = NULL
    结果:链表:1->2


3、插入 3:

  • 创建新节点 node,将值 3 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 遍历链表,直到找到最后一个节点(temp->next == NULL)
  • 将新节点连接到最后一个节点:temp->next = node
  • 设置 node->next = NULL
    结果:链表:1->2->3


现在您可以用这个详细的示例在草稿纸上代入演算。这将帮助您更好地理解头插入和尾插入的过程。

在演算时,请注意以下关键点:

  • 对于头插入法,新节点总是插入到链表的开头。因此,当我们插入一个新节点时,原来的头节点需要变成新节点的下一个节点。这就是为什么我们需要将原头节点的指针赋给新节点的 next 指针,并将新节点设置为头节点。
  • 对于尾插入法,新节点总是插入到链表的末尾。因此,我们需要遍历链表,找到最后一个节点,然后将新节点连接到最后一个节点。最后一个节点的 next 指针需要指向新节点,新节点的 next 指针设置为 NULL。


通过对这些示例进行演算,您将更深入地了解头插入和尾插入的概念和实现。如果您在演算过程中遇到任何困惑或问题,请随时向我求助。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-10 21:36:49 | 显示全部楼层
a905448839 发表于 2023-4-10 21:29
还能再详细点吗?我想在草稿纸自己代入演算以下。。

不看我的吗
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-4-10 22:14:28 | 显示全部楼层

看,吸收各位大佬的精华!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-4-10 22:23:39 | 显示全部楼层
isdkz 发表于 2023-4-10 21:35
当然可以。让我们用一个详细的示例,分步骤解释头插入和尾插入的过程。

首先,我们将使用头插入法。我 ...

头插入问题:
插入1:
设置 node->next = NULL
结果:链表:1
插入2:
设置新头节点的下一个节点为原头节点:node->next = temp
结果:链表:2->1
插入 3:
设置新头节点的下一个节点为原头节点:node->next = temp
结果:链表:3->2
问题1:
插入数据3的时候 链表是(3->2)   而这个2 和插入数据2里(2->1)的2是一个结构体吗?
为什么能程序能知道插入数据3(3->2)的这个2 是插入数据2那个(2->1)的那个2呢?
简单点来说 就是插入数据3后结果是3->2  这个2凭什么会去指向1 变成3->2->1?
问题2:
如果成立 应该是node链表为3->2->1  和传入进来的struct Node **head 或者说是 main主函数里的struct Node *head_one有什么关系呢?  他们不是单独的两个链表吗? 是怎么返回出去值的呢?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-10 22:30:21 | 显示全部楼层
a905448839 发表于 2023-4-10 22:23
头插入问题:
插入1:
设置 node->next = NULL

问题1:
在插入数据3时,链表确实为3->2,但是这里我漏掉了一步。我为您更正一下头插入过程的步骤:

1、插入 1:

  • 创建新节点 node,将值 1 赋给 node->value
  • 检查链表是否为空(*head == NULL),链表为空
  • 设置 node 为头节点:*head = node
  • 设置 node->next = NULL

结果:链表:1

2、插入 2:

  • 创建新节点 node,将值 2 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 设置新节点为头节点:*head = node
  • 设置新头节点的下一个节点为原头节点:node->next = temp

结果:链表:2->1

3、插入 3:

  • 创建新节点 node,将值 3 赋给 node->value
  • 检查链表是否为空(*head != NULL),链表不为空
  • 将临时指针 temp 指向头节点:temp = *head
  • 设置新节点为头节点:*head = node
  • 设置新头节点的下一个节点为原头节点:node->next = temp

结果:链表:3->2->1

当我们插入数据3时,我们将当前的头节点保存在临时指针temp中(temp = *head,此时temp指向节点2)。

然后,我们将新创建的节点3设置为新的头节点(*head = node)。

最后,我们将新头节点(节点3)的下一个节点设置为原头节点(节点2),即node->next = temp。这样,链表就变成了3->2->1。

问题2:

在main函数里,我们创建了一个链表头指针变量head_one。

当我们调用头插入函数insertNode_one时,我们传递head_one的地址作为参数(&head_one)。

因此,我们在函数内部操作的是head_one的指针,所以对其所指向的链表进行的任何更改都会反映到main函数中的head_one变量上。

这就是为什么当我们在函数insertNode_one内部更新链表时,main函数中的head_one链表也会相应地更新。

在函数执行完毕后,head_one现在指向已更新的链表。

这两个链表不是独立的,实际上它们是同一个链表,只是在函数调用时通过传递指针来实现它们之间的连接。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-4-10 23:02:07 | 显示全部楼层
isdkz 发表于 2023-4-10 22:30
问题1:
在插入数据3时,链表确实为3->2,但是这里我漏掉了一步。我为您更正一下头插入过程的步骤:

原问题1:最后,我们将新头节点(节点3)的下一个节点设置为原头节点(节点2),即node->next = temp。这样,链表就变成了3->2->1。

由于在草稿纸演算问题,我一直理解node->next = temp表达的是 把temp节点的值赋值给了当前node单链表节点指向的下一个节点的值。其实应该这样理解成 当前node单链表节点指向的下一个节点是temp这个节点才对吗?这个=不是赋值 因为team是一个节点指针 是地址,相当于把node当前单链表节点的下一个节点指向了team的地址?可以这样理解吗?


原问题2:
我始终无法明白,在头插入函数里,一直在操作node,node的链表值在改变和外面传进来的*head_one值有什么关系?如果他们是一个单链表,我也没有在代码里找到node指向*head_one。为什么node改变*head_one就会改变呢?
我只在当第一次插入的时候 if(*head == NULL)的判断语句里看到*head = node;
相当于外面head_one的地址已经改变成了node的地址是吧?
所以后面无论怎么改变node 不去改变*head也没关系,*head已经指向node了,我们只用修改node就是了吗?

问题3:
指针的地址变量是在整个文件中都是有效的吗?无论在哪个函数都可以访问吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-10 23:52:06 | 显示全部楼层
a905448839 发表于 2023-4-10 23:02
原问题1:最后,我们将新头节点(节点3)的下一个节点设置为原头节点(节点2),即node->next = temp。这 ...

原问题1:
是的,您的理解是正确的。node->next = temp 的意思是将当前 node 节点的下一个节点设置为 temp 指向的节点。在这里,= 是指将 temp 指针所存储的地址赋给 node->next,因此,当前 node 节点的下一个节点就是 temp 指向的节点。

原问题2:
您的理解是正确的。在头插入函数中,当链表为空(即 *head == NULL)时,我们将 *head 指向 node,此时 head_one 的地址被更改为 node 的地址。之后,无论如何改变 node,我们只需要更新 *head 即可。实际上,在头插入函数中,我们将 *head 设置为新的头节点,这意味着我们已经将链表的头节点更新为新插入的节点。因此,当我们在函数内部修改 node 时,链表实际上已经更新了,而我们不需要再次改变 *head。

问题3:
指针变量的作用域(可访问性)取决于它们声明的位置。如果指针变量在函数内部声明,则只能在该函数内访问。如果指针变量在函数外部声明,则在整个文件中都可以访问。然而,要在其他函数中访问一个指针变量,您需要将该指针作为参数传递给其他函数,就像在头插入和尾插入函数中传递链表头指针一样。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2023-4-10 23:53:53 | 显示全部楼层
isdkz 发表于 2023-4-10 23:52
原问题1:
是的,您的理解是正确的。node->next = temp 的意思是将当前 node 节点的下一个节点设置为 te ...

太感谢你!!!!!!!!!I LOVE YOU !
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-4-11 00:55:29 | 显示全部楼层
isdkz 发表于 2023-4-10 21:35
当然可以。让我们用一个详细的示例,分步骤解释头插入和尾插入的过程。

首先,我们将使用头插入法。我 ...

尾插入问题:
插入1:
node->value = value;  node->value == 1
此时*head为NULL
*head = node;                     *head == 1
node->next = NULL;     node->next == 1->NULL

插入2:
node->value = value;  node->value == 2
此时*head不为NULL
temp = *head;                       temp == 1;
while(temp->next != NULL)
{
          temp = temp->next;
}
循环结束后:temp->next == NULL    相当于1->NULL
temp->next = node;      temp->next == 1->2
node->next = NULL;      node->next == 2->NULL

插入3:
node->value = value;  node->value == 3
此时*head不为NULL
temp = *head;                       temp == 1;
while(temp->next != NULL)
{
          temp = temp->next;
}
循环结束后:temp->next == NULL    相当于1->NULL
temp->next = node;      temp->next == 1->3
node->next = NULL;      node->next == 3->NULL

我这样演算好像是有问题的  是插入数据3的时候我演算这个出问题
因为(在插入数据1:*head = node;                *head == 1)
因为(在插入数据2:temp = *head;                temp == 1;)//此时*head为插入数据1中的值为1。
所以(在插入数据2:temp->next = node;      temp->next == 1->2)
所以 此时temp和*head都为 1
然后(在插入数据3:temp = *head;                 temp == 1;)
最后在插入数据3中,temp和*head都为1 就出问题了。。。

所以我认为在输入数据3中  temp节点里的那个值应该是,但是思来想去无法理解  有点绕晕了 能给我解释以下吗?谢谢!(好像和之前请教你的地址问题有关是吗?)
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-11 08:02:38 | 显示全部楼层
a905448839 发表于 2023-4-11 00:55
尾插入问题:
插入1:
node->value = value;  node->value == 1

在您的演算中,插入3时有一点问题。

插入1:

  • node->value = 1
  • *head 为 NULL
  • *head = node; 此时 *head 指向 1
  • node->next = NULL; 此时链表为:1->NULL


插入2:

  • node->value = 2
  • *head 不为 NULL
  • temp = *head; 此时 temp 指向 1
  • while 循环找到链表尾部,temp 指向 1
  • temp->next = node; 此时链表变为:1->2
  • node->next = NULL; 此时链表为:1->2->NULL


插入3:

  • node->value = 3
  • *head 不为 NULL
  • temp = *head; 此时 temp 指向 1
  • while 循环找到链表尾部,temp 指向 2(因为 2 是链表中的最后一个节点)
  • temp->next = node; 此时链表变为:1->2->3
  • node->next = NULL; 此时链表为:1->2->3->NULL


在插入3时,您需要在 while 循环中找到链表尾部,即找到最后一个节点。在插入3时,链表为:1->2->NULL,最后一个节点为2。

所以,当您在 while 循环中遍历链表时,temp 指针最终将指向节点2。然后,将新节点3连接到节点2,链表变为:1->2->3->NULL。

关键在于理解 while 循环的目的是找到链表尾部,而不是找到 NULL。这就是为什么在插入3时,temp 指针最终指向2,而不是1。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-4-11 21:30:41 | 显示全部楼层
isdkz 发表于 2023-4-11 08:02
在您的演算中,插入3时有一点问题。

插入1:

此代码尾插入中,用的是循环找到最后一个节点,每次插入都要循环一次呢。有没有更省效率的方法呢?我记得小甲鱼有说过可以定义一个永远指向尾节点的指针,代码如何写呢?详细的逻辑思维能告知一下吗?谢谢!
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-11 21:35:07 | 显示全部楼层
a905448839 发表于 2023-4-11 21:30
此代码尾插入中,用的是循环找到最后一个节点,每次插入都要循环一次呢。有没有更省效率的方法呢?我记得 ...

当然可以优化尾插入的效率。通过定义一个永远指向尾节点的指针,可以将尾插入的时间复杂度降低为 O(1)。

以下是如何实现这一优化的详细逻辑思维和代码:

  • 定义一个尾节点指针 tail。
  • 每次插入新节点时,直接将新节点插入到 tail 后面。
  • 更新 tail 为新插入的节点。


以下是相应的 C 代码:
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int value;
    struct Node *next;
};

void insert_tail(struct Node **head, struct Node **tail, int value) {
    struct Node *node = (struct Node *) malloc(sizeof(struct Node));
    node->value = value;
    node->next = NULL;

    if (*head == NULL) {
        *head = node;
        *tail = node;
    } else {
        (*tail)->next = node;
        *tail = node;
    }
}

void print_list(struct Node *head) {
    while (head != NULL) {
        printf("%d->", head->value);
        head = head->next;
    }
    printf("NULL\n");
}

int main() {
    struct Node *head = NULL;
    struct Node *tail = NULL;

    insert_tail(&head, &tail, 1);
    insert_tail(&head, &tail, 2);
    insert_tail(&head, &tail, 3);
    insert_tail(&head, &tail, 4);

    print_list(head);

    return 0;
}

在这个代码示例中,我们定义了一个名为 tail 的指针,它总是指向链表的尾节点。

每次插入一个新节点时,我们直接将其插入到 tail 指向的节点后面,然后更新 tail 为新插入的节点。

这样,尾插入操作的时间复杂度就降低为 O(1) 了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2023-4-12 00:35:13 From FishC Mobile | 显示全部楼层
isdkz 发表于 2023-4-11 21:35
当然可以优化尾插入的效率。通过定义一个永远指向尾节点的指针,可以将尾插入的时间复杂度降低为 O(1)。
...

这个tail尾节点指针能在尾插入函数的代码块里定义和使用吗?不从it main函数里传入能实现吗?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-12 00:38:49 | 显示全部楼层
a905448839 发表于 2023-4-12 00:35
这个tail尾节点指针能在尾插入函数的代码块里定义和使用吗?不从it main函数里传入能实现吗?

要在尾插入函数内部定义和使用 tail 尾节点指针,需要将其声明为静态变量。

这样,tail 的值在函数的多次调用之间会保持。请注意,这种方法不太适用于处理多个链表,因为所有链表将共享同一个 tail 指针。

然而,在处理单个链表的情况下,它可以很好地工作。

以下是修改后的代码:
#include <stdio.h>
#include <stdlib.h>

struct Node {
    int value;
    struct Node *next;
};

void insert_tail(struct Node **head, int value) {
    struct Node *node = (struct Node *) malloc(sizeof(struct Node));
    node->value = value;
    node->next = NULL;

    static struct Node *tail = NULL;

    if (*head == NULL) {
        *head = node;
        tail = node;
    } else {
        tail->next = node;
        tail = node;
    }
}

void print_list(struct Node *head) {
    while (head != NULL) {
        printf("%d->", head->value);
        head = head->next;
    }
    printf("NULL\n");
}

int main() {
    struct Node *head = NULL;

    insert_tail(&head, 1);
    insert_tail(&head, 2);
    insert_tail(&head, 3);
    insert_tail(&head, 4);

    print_list(head);

    return 0;
}

在这个修改后的代码中,我们将 tail 声明为静态变量。这意味着它将在整个程序执行过程中一直存在,并且只在函数的第一次调用时被初始化。

这样,我们就可以在 insert_tail 函数内部定义和使用 tail 指针了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-18 15:41:49 | 显示全部楼层
isdkz 发表于 2023-4-10 21:35
当然可以。让我们用一个详细的示例,分步骤解释头插入和尾插入的过程。

首先,我们将使用头插入法。我 ...

chat GPD?
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2023-4-18 15:42:51 | 显示全部楼层

不是,是chatgpt
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-12-23 23:26

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表