C++ 头插法建立单链表,单链表原地逆置以及尾插法建立单链表

#include <iostream>
#include <stdlib.h>
#include <cstdio>
typedef int ElemType;
using namespace std;
//单链表的结构体
typedef struct LNode
{
    ElemType data;
    LNode *next;
}LNode,*LinkList;
//头插法建立单链表
LinkList List_HeadInsert(LinkList &L)
{
    L=(LNode *)malloc(sizeof(LNode));
    L->next=NULL;
    LNode *s;//s为新插入结点的指针
    int x;
    scanf("%d",&x);
    while(x!=9999){
        s=(LNode *)malloc(sizeof(LNode));//为新结点分配一片内存空间,并将该内存空间的返回地址赋值给s
        s->data=x;
        s->next=L->next;//将s插入到头结点之后
        L->next=s;
        scanf("%d",&x);
    }
    return L;
}
//尾插法建立单链表
LinkList List_TailInsert(LinkList &L)
{
    L=(LNode *)malloc(sizeof(LNode));
    L->next=NULL;
    LNode *s;
    LNode *r=L;//r为尾指针
    int x;
    scanf("%d",&x);
    while(x!=9999)
    {
        s=(LNode *)malloc(sizeof(LNode));
        s->data=x;
<think>嗯,用户问的是如何用头插法单链表原地。我得先回忆一下头插法单链表的基本结构。头插法通常是在链表头部插入新节点,比如创建链表的时候,每次新节点都插在头节点后面。那的话,可能需要遍历原链表,然后把每个节点依次用头插法插入到一个新的链表中,这样最后新链表就是序的。可是题目里说是原地,也就是不能使用额外的空间,所以不能新建链表,得在原链表上操作。 首先,我需要明确单链表的结构。单链表的每个节点包含数据和指向下一个节点的指针的话,就是把每个节点的指针指向前一个节点,而不是后一个。比如原来的链表是1->2->3->4->null,后应该是4->3->2->1->null。 用头插法的话,可能需要从头开始遍历原链表,把每个节点取下来,然后插入到链表的头部。比如,初始时新建一个空节点作为头节点。然后原链表的第一个节点1被取下,插入到头节点后面,变成头->1->null。接着原链表的第二个节点2被取下,插入到头节点后面,变成头->2->1->null,依此类推,直到所有节点都插入到头节点后面,这样原来的链表就被了。 不过这里要注意的是,因为是原地,所以不能使用额外的头节点,必须利用原来的链表结构。可能需要调整指针的指向,而不是实际创建新的节点。所以可能需要三个指针:当前节点current,下一个节点next,以及头节点之后的第一个节点。然后遍历链表,每次将current节点插入到头部。 让我再仔细想想。假设原链表头节点是head,初始时head指向第一个节点。的过程需要把每个节点移动到头部。比如,初始链表是head->1->2->3->4->null。第一步,取出节点1,插入到head后面,但其实已经是head后面了,所以可能不需要动。不对,可能应该从第二个节点开始处理。或者,可能需要把后面的节点逐个插入到head后面。 正确的步骤应该是这样的:将原链表的第一个节点保留,然后从第二个节点开始,依次取出,插入到head节点之后。例如,原链表是1->2->3->4,头节点指向1。时,取出2,插入到head后面,变成head->2->1->3->4。然后取出3,插入到head后面,变成head->3->2->1->4,最后取出4,插入到head后面,得到head->4->3->2->1。这样整个链表了。这个过程就是头插法,每次将后面的节点插入到头部,从而实现。 具体实现的话,需要三个指针:current指向当前要处理的节点,prev指向当前处理节点的前一个节点,以及一个临时指针保存下一个节点。或者,另一种方法是,从头节点开始,将每个节点的next指针指向前一个节点,但这需要遍历时记录前一个节点,这样可能需要三个指针:prev、current、next。这可能与头插法的思路不同。 不过用户的问题明确提到用头插法,所以应该按照头插法的思路来。具体步骤可能如下: 1. 初始化一个新的头节点,或者使用原链表的头节点。 2. 遍历原链表,依次取出每个节点,将其插入到新头节点的后面。 3. 最终,新头节点后的链表即为后的链表。 但因为是原地,不能使用额外的头节点,所以需要调整原链表指针。正确的做法应该是: 1. 检查链表是否为空或只有一个节点,如果是,直接返回。 2. 定义两个指针:current和next。current初始化为原链表的第一个节点,next保存current的下一个节点。 3. 将原头节点的next为null,因为后原第一个节点将成为最后一个节点。 4. 遍历链表,每次取出current的下一个节点next,将其next指向原头节点的下一个节点,然后将原头节点的next指向这个next节点。这样相当于将next节点插入到头部。 5. 继续直到所有节点都被处理。 或者,另一种更清晰的步骤: 1. 如果链表为空或只有一个节点,直接返回头节点。 2. 初始化三个指针:prev初始化为null,current初始化为头节点,next初始化为null。 3. 遍历链表,每次将current的next保存到next变量,然后将current的next指向prev,实现反转。然后prev和current分别向前移动一位。 4. 最后,头节点变为prev。 不过这可能不是头插法,而是常规的反转链表的方法。用户的问题明确要求用头插法,所以需要换一种思路。 正确的头插法原地步骤应该是: 假设原链表头节点是head,头节点后的第一个节点是node1,第二个是node2,依此类推。 1. 将原链表的第一个节点(head->next)作为当前节点current。 2. 保存current的下一个节点next_node。 3. 将current的next指向head->next的下一个节点(即原来的第二个节点的下一个,可能不需要?) 4. 将current插入到head之后,即current->next = head->next; head->next = current; 5. 重复这个过程,直到所有节点都被处理。 可能更详细的步骤: 原链表:head -> 1 -> 2 -> 3 -> 4 -> null 步骤: - 取出节点1,插入到head之后,此时链表还是head ->1 -> ... 没变化,所以需要从第二个节点开始处理。 - 取出节点2,插入到head之后,变成head ->2 ->1 ->3->4->null - 取出节点3,插入到head之后,变成head->3->2->1->4->null - 取出节点4,插入到head之后,变成head->4->3->2->1->null 这样整个链表了。所以具体的步骤应该是: 1. 检查链表是否为空或只有头节点,直接返回。 2. 定义指针p初始指向头节点的下一个节点(即第一个有效节点),然后将头节点的next为null,因为后第一个节点会成为最后一个。 3. 但是根据头插法,可能需要保持头节点,并不断将后续节点插入到头部之后。比如: 初始链表:head ->1 ->2 ->3 ->4 步骤: - 保存head的next(即1),然后从第二个节点开始处理。 - 取出节点2,插入到head之后:head ->2 ->1 ->3 ->4 - 取出节点3,插入到head之后:head ->3 ->2 ->1 ->4 - 取出节点4,插入到head之后:head ->4 ->3 ->2 ->1 这样完成。但这里的关键是如何遍历并取出每个节点。具体来说,可能需要: 初始时,current = head->next(即1),然后断开head->next,因为之后要将节点插入到head后面。所以: 1. 定义current = head->next; 2. head->next = null; 这样原链表被断开,剩下的current是原链表的第一个节点。 3. 然后遍历current链表,将每个节点用头插法插入到head之后。 但是这样的话,原链表的节点会被逐个插入到head之后,从而形成序。例如: 原链表current是1->2->3->4->null 处理步骤: - 取出节点1,插入到head之后:head->1->null,current变为2->3->4 - 取出节点2,插入到head之后:head->2->1->null,current变为3->4 - 取出节点3,插入到head之后:head->3->2->1->null,current变为4 - 取出节点4,插入到head之后:head->4->3->2->1->null 这样,原链表就被了。这种方法确实是头插法,并且是原地,因为没有使用额外的空间,只是改变了节点的指针指向。 所以具体的算法步骤应该是: 1. 如果链表为空或只有一个节点,返回头节点。 2. 定义current指针指向原链表的第一个有效节点(即head->next)。 3. 将头节点的next为null,断开与原链表的连接。 4. 遍历current链表,直到current为null: a. 保存current的下一个节点next_node。 b. 将current的next指向头节点的next(即当前头节点后面的部分)。 c. 将头节点的next指向current。 d. current移动到next_node。 5. 最终,头节点后的链表即为后的链表。 不过需要注意,这里的头节点是否为虚拟头节点。如果原链表没有虚拟头节点,即头节点就是第一个数据节点,那么步骤会有所不同。例如,如果链表是1->2->3->4,头节点是1,那么后应该是4->3->2->1。此时需要用头插法将每个后续节点插入到链表的头部。 这时候的步骤应该是: 1. 初始化三个指针:prev = null, current = head, next = null。 2. 遍历链表: while current != null: next = current.next current.next = prev prev = current current = next 3. 头节点变为prev。 但这样是常规的反转方法,不是头插法。用户的问题要求用头插法,所以可能需要另一种方式。 假设原链表是head ->1->2->3->4,其中head是虚拟头节点,那么用头插法的步骤如下: 初始:head ->1->2->3->4->null 过程: - 取出1,插入到head之后:此时链表结构没变,因为1已经是第一个节点。因此需要从第二个节点开始处理。 - 取出2,插入到head之后,得到head->2->1->3->4->null - 取出3,插入到head之后,得到head->3->2->1->4->null - 取出4,插入到head之后,得到head->4->3->2->1->null 这样,完成。具体实现时需要: 1. 原链表有虚拟头节点head,第一个节点是p = head->next。 2. 如果p为空或p->next为空,直接返回。 3. 定义current = p->next(即第二个节点),然后断开p->next,让p->next = null。 4. 然后循环处理current: while current != null: 保存current的下一个节点next_node = current->next 将current插入到head之后:current->next = head->next head->next = current current = next_node 这样处理的话,原链表的第一个节点p会被保留,然后后面的节点都被插入到头部,导致顺序。但这样会导致原第一个节点p变成最后一个节点,而其他节点序插入到前面,最终链表结构正确吗? 例如,原链表是head->1->2->3->4 步骤: - p = head->next(1) - head->next = null(此时head->next是null) - current = p->next(2) - p->next = null(断开原链表) 然后处理current=2: next_node = 3 current->next = head->next(null) head->next = 2,此时链表是head->2->null 然后current=3: next_node=4 current->next = head->next(2) head->next =3,链表变为head->3->2->null current=4: next_node=null current->next = head->next(3) head->next=4,链表变为head->4->3->2->null 此时原p节点1仍然存在,但head的链表是4->3->2,而原来的p节点1还留在哪里?这时候发现遗漏了原第一个节点1,因为在断开head->next之后,p节点1没有被处理,所以最终链表会是head->4->3->2,而节点1没有被连接到链表中,导致丢失。 这说明上述步骤有问题,没有正确处理原第一个节点。正确的做法应该是将原第一个节点也插入到head之后,但需要调整步骤。 正确的处理方式应该是:原链表的所有节点,包括第一个节点,都通过头插法重新插入到head之后。但这样的话,原第一个节点会被移动到后面。例如: 原链表head->1->2->3->4 步骤: - current = head->next(1) - head->next = null - 然后循环处理current: while current != null: 保存next_node = current->next 将current插入到head之后:current->next = head->next head->next = current current = next_node 这样,处理第一个节点1时: head->next = null,插入后变成head->1->null current变为2 处理节点2: 插入到head之后,变成head->2->1->null current变为3 处理节点3: 插入到head之后,变成head->3->2->1->null current变为4 处理节点4: 插入到head之后,变成head->4->3->2->1->null current变为null,结束循环。 这样整个链表就被正确了。所以关键点在于,从头节点后的第一个节点开始,依次取出每个节点,插入到head节点之后。这样原来的第一个节点最后会被移动到末尾,从而实现。 因此,正确的算法步骤是: 1. 如果链表为空或只有一个节点,直接返回。 2. 定义current指针指向头节点的下一个节点(即第一个有效节点)。 3. 将头节点的next为null,断开链表。 4. 循环处理current节点: a. 保存current的下一个节点为next_node。 b. 将current的next指向头节点的当前下一个节点。 c. 将头节点的next指向current。 d. 将current更新为next_node。 5. 循环直到current为null,此时链表完成。 例如,原链表:head ->1->2->3->4->null 步骤: - current =1,head->next=null - 处理current=1: next_node=2 current->next = head->next(null) head->next =1 → head->1->null current=2 - 处理current=2: next_node=3 current->next = head->next(1) head->next =2 → head->2->1->null current=3 - 处理current=3: next_node=4 current->next =2 head->next=3 → head->3->2->1->null current=4 - 处理current=4: next_node=null current->next=3 head->next=4 → head->4->3->2->1->null current=null,结束循环 这样,最终链表,且没有使用额外空间,符合原地的要求。 总结一下,使用头插法原地单链表的步骤是: 1. 初始化current指针为头节点的下一个节点。 2. 将头节点与原链表断开(head->next = null)。 3. 遍历current链表,逐个将节点插入到头节点之后,形成序。 这样就能实现原地,时间复杂度为O(n),空间复杂度为O(1)。</think>单链表原地(不额外开辟空间)可以通过头插法实现。以下是详细步骤和原理说明: --- ### **核心思路** 将原链表的节点逐个"摘下",用头插法重新插入到头节点之后。每次插入的新节点都会成为链表的新第一个节点,最终实现整体。 --- ### **具体步骤** 1. **边界处理** 若链表为空或仅有一个节点,直接返回。 2. **初始化指针** - `current`:指向原链表一个有效节点(即 `head->next`)。 - **断开原链表**:将头节点的 `next` 空(`head->next = NULL`),准备重新构建链表。 3. **遍历并头插** 循环处理每个节点: ```plaintext while (current != NULL) { 1. 保存 current 的下一个节点(next_node = current->next); 2. 将 current 插入头节点之后: - current->next = head->next; - head->next = current; 3. current 移动到下一个节点(current = next_node); } ``` --- ### **示意图** 以链表 `1 → 2 → 3 → 4` 为例(假设存在虚拟头节点 `head`): | 步骤 | 操作 | 链表状态 | |------|----------------------|-------------------------| | 初始 | `head → 1 → 2 → 3 → 4` | | | 1 | 处理节点1 | `head → 1` | | 2 | 处理节点2 | `head → 2 → 1` | | 3 | 处理节点3 | `head → 3 → 2 → 1` |
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值