python链表——实现链表逆序的三种方式:就地逆序,递归,插入法

本文介绍了单链表逆序的三种方法,包括就地逆序、递归逆序和插入法逆序。详细阐述了每种方法的思路、代码实现及优缺点。三种方法时间复杂度均为O(N),就地逆序和插入法逆序性能较高,递归逆序代码逻辑简单但性能较低。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

输入:1 -> 2 -> 3 ->4
输出:4 -> 3 -> 2 -> 1

一、就地逆序
思路:
1、引入外部操作节点的变量pre, cur, nex,分别代表前一个节点、当前节点、下一个节点。
2、单链表的每个节点只有一个指针,故对每个节点的指针进行2次操作,第1次指针操作,将当前节点指针cur.next值临时保存到nex,即nex = cur.next, 第2次指针操作,将当前的指针cur.next指向其前一个节点pre, 即cur.next = pre,这样就实现了对单个节点的操作。
3、操作节点的变量移动,cur指向下一个操作节点,即cur = nex,这样通过while循环即可实现对每个节点操作。
4、特殊情况:链表为空链表、链表为单节点,直接返回即可,无需逆序操作

代码如下:

def reverse(head):
    # 特殊情况:头节点为空或者为单节点,直接返回
    if head is None or head.next is None:
        return head
    else:
        # 移动cur到头节点
        cur = head
        # 操作当前节点指针, 2次操作
        nex = cur.next
        cur.next = None
        
        # 移动cur到下一个节点
        pre = cur
        cur = nex
        # 循环操作节点
        while cur.next:
            # 操作当前节点指针, 2次操作
            nex = cur.next
            cur.next = pre
            # 移动cur到下一个节点
            pre = cur
            cur = nex
        # 最后一个操作节点, 指针指向其前一个pre,同时将其设为头节点
        cur.next = pre
        head = cur
    return head

评价:链表每个节点遍历1次,故时间复杂度为O(N), 只引入3个外部变量作为临时存储,性能方面较高,不足之处,在于代码逻辑没有那么清晰明了,需要理解三个外部变量是如何移动的,2次指针操作是如何变化的。

二、递归逆序
思路:
1、首先将1 -> 2 -> 3 ->4变成1 -> 4-> 3 -> 2(其中2 -> 3 ->4变成4-> 3 -> 2,采用递归方式实现)
2、再将head头节点1移到尾巴,作为尾结点,由于上一个尾结点2,是原先的head.next,此时将2的next指针指向头节点,即head.next.next = head,然后尾结点指向空,即head.next = None
在这里插入图片描述
3、特殊情况:头节点为空或者只有1个节点

代码:

def recursiveReverse(head):
	#  头节点为空或者只有1个节点
    if head is None or head.next is None:
        return head
    else:
        # 先反转后边的节点:1->4->3->2,递归实现
        new_head = recursiveReverse(head.next)
        # 然后将head移动到节点head.next之后,2次指针操作
        head.next.next = head
        head.next = None
        # 参数为头结点,所以返回的为new_head新的头结点
        return new_head

评价:优点是代码逻辑比较简单,链表每个节点遍历1次,故时间复杂度为O(N), 不足在于,由于采用递归,每次的递归的值需要push到栈中,所以性能较低

三、插入法逆序
思路:
1、1 -> 2 -> 3 ->4变成2 -> 1 -> 3 ->4,再变成3 -> 2 -> 1 -> 4,最后4 -> 3 -> 2 -> 1
2、先操作头节点,2次指针操作,第一次赋值给外部变量cur=head.next, 由于头节点变为尾结点,即head.next=None
3、从第二个节点开始,每次都将遍历到的节点,插入到头节点前边,作为新的头节点,2次指针操作和头节点指针指定操作,即2次指针操作:nex=cur.next, cur.next=head, 指定头节点:head=cur
4、移动cur到下一个节点,即cur=nex
5、特殊情况:头节点为空,或者单个节点,直接返回
代码:

def insertReverse(head):
	# 特殊情况:头节点为空,或者只有单个节点
    if head is None or head.next is None:
        return
    # 操作头节点, 2次指针操作,头节点head.next赋值给cur, 由于头节点最终要成为尾结点,其指向为空
    cur = head.next
    head.next = None
    # 操作第二个及之后的节点,每次遍历到的节点都放到头节点处
    while cur:
        nex = cur.next
        cur.next = head
        head = cur
        cur = nex
    return head

评价:每个节点都遍历一次,时间复杂度为O(N),引入外部变量cur, nex作为临时存储,性能较高,代码逻辑方面,每个节点仍然是2次指针操作,以及操作对象的指针nex, cur的移动,与就地逆序类似,相对简单。

# 测试
link = LinkedList()
print("=========== before =============")
link.add(1)
link.add(2)
link.add(3)
link.add(4)
link.print_list()
print("=========== after ==============")
new_head = reverse(link.head)
while new_head:
    print(new_head.data)
    new_head = new_head.next
# 结果
=========== before =============
1
2
3
4
=========== after ==============
4
3
2
1
<think>好的,用户想了解链表倒序实现方法或算法。我需要先回顾相关知识,并结合提供的引用资料来整理答案。首先,链表倒序有两种常见方法:循环反转和递归反转。用户提供的引用中,引用[2]提到了这两种方法,而引用[4]和[5]分别讨论了就地倒序和利用栈辅助的方法。 先理清思路。链表倒序的关键在于改变节点间的指针指向。对于单链表来说,每个节点的next指针原本指向下一个节点,倒序后需要指向前一个节点。需要注意的是,处理过程中不能断链,否则会丢失后续节点的信息。 循环反转法通常需要三个指针:pre、current、next。pre记录前一个节点,current当前节点,next保存下一个节点。每次迭代将current的next指向pre,然后三个指针依次后移。引用[2]和引用[4]都提到了这种方法,但引用[4]中的示例代码可能有问题,需要指出正确写法。 递归法则是从链表末尾开始反转,每次递归处理当前节点的下一个节点,将下一个节点的next指向当前节点,然后断开当前节点的原next,避免循环。需要处理递归终止条件,通常是当前节点或下一个节点为空。 另外,引用[5]提到使用栈来辅助倒序,这种方法虽然空间复杂度较高,但思路简单,适合理解倒序的概念。将链表元素压入栈,再依次弹出即可得到逆序结果。 接下来,需要检查引用资料中的代码示例是否正确。比如引用[4]中的代码可能存在问题,导致断链,需要指出错误并提供正确的实现。例如,正确的循环反转应该正确移动pre、current和next指针,并确保每一步都正确更新指针,避免丢失后续节点。 在组织回答时,应该先介绍基本概念,然后分步骤讲解循环法和递归法的实现,最后可以提及其他方法如栈辅助。同时,要注意代码的正确性,并引用相关资料中的正确示例或指出错误。 可能还需要比较不同方法的时间复杂度和空间复杂度。循环法和递归法的时间复杂度都是O(n),递归法的空间复杂度由于递归栈也是O(n),而循环法是O(1)。栈辅助方法同样空间复杂度O(n)。 用户可能对如何选择这些方法有疑问,所以可以在相关问题中提出优化方法或应用场景的问题。同时,需要确保回答结构清晰,分点说明,帮助用户逐步理解实现过程。</think>### 链表倒序实现算法详解 链表倒序的核心在于**修改指针指向顺序**,使链表方向完全反转。以下是两种主流实现方法及其代码示例: --- #### 一、循环反转法(时间复杂度 $O(n)$,空间复杂度 $O(1)$) **实现步骤**: 1. 初始化三个指针: - `pre`:记录前驱节点,初始为 `null` - `current`:当前操作节点,初始为头节点 - `next`:临时保存后续节点 2. 遍历链表,逐节点反转指针方向: - 保存 `current.next` 到 `next`(避免断链) - 将 `current.next` 指向 `pre`(反转核心操作) - `pre` 和 `current` 向后移动一位 3. 最终 `pre` 成为新头节点 **代码示例**(Python): ```python class ListNode: def __init__(self, val=0, next=None): self.val = val self.next = next def reverse_list(head: ListNode) -> ListNode: pre = None current = head while current: next_node = current.next # 保存下一节点 current.next = pre # 反转指针 pre = current # pre后移 current = next_node # current后移 return pre # 返回新头节点 ``` **关键点**:必须通过 `next_node` 暂存后续节点,否则会导致断链[^4]。 --- #### 二、递归反转法(时间复杂度 $O(n)$,空间复杂度 $O(n)$) **实现步骤**: 1. 递归链表末尾(终止条件:`head` 或 `head.next` 为空) 2. 从末尾开始逐层返回,修改指针方向: - 将当前节点的下一节点的 `next` 指向当前节点 - 断开当前节点的原 `next` 指针 **代码示例**: ```python def reverse_list_recursive(head: ListNode) -> ListNode: if not head or not head.next: # 终止条件 return head new_head = reverse_list_recursive(head.next) head.next.next = head # 反转指向 head.next = None # 断开原链接 return new_head ``` **注意**:递归深度与链表长度成正比,长链表可能导致栈溢出[^2]。 --- #### 三、其他方法对比 1. **栈辅助法**(空间复杂度 $O(n)$) - 遍历链表将元素压入栈 - 依次弹出元素生成新链表 - 优点:代码简单,适合逆向输出场景[^5] $$ \text{时间复杂度} = O(2n), \quad \text{空间复杂度} = O(n) $$ --- ### 常见问题分析 1. **断链问题**:循环法中若未正确保存 `next_node`,会导致后续节点丢失[^4] 2. **尾节点处理**:递归法需手动断开原头节点的 `next`,否则形成环 3. **边界条件**:空链表或单节点链表需直接返回原头节点 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值