Leetcode 剑指 Offer II 026. 重排链表

题目难度: 中等

原题链接

今天继续更新 Leetcode 的剑指 Offer(专项突击版)系列, 大家在公众号 算法精选 里回复 剑指offer2 就能看到该系列当前连载的所有文章了, 记得关注哦~

题目描述

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0 → L1 → … → Ln-1 → Ln 
请将其重新排列后变为:

L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例 1:

  • 输入: head = [1,2,3,4]
  • 输出: [1,4,2,3]

示例 2:

  • 输入: head = [1,2,3,4,5]
  • 输出: [1,5,2,4,3]

提示:

  • 链表的长度范围为 [1, 5 * 10^4]
  • 1 <= node.val <= 1000

题目思考

  1. 如何做到 O(1) 空间复杂度?

解决方案

思路
  • 分析题目, 最容易想到的做法就是把链表节点用数组存储, 然后使用头尾双指针交替指向对方
  • 不过这种做法的空间复杂度是 O(N), 如何优化呢?
  • 注意题目重排的方式是 1->N, 2->N-1, 以此类推, 相当于左半部分和翻转的右半部分进行合并
  • 所以我们可以分为以下三步实现:
  1. 将链表分为左右两半部分, 并断开之间的连接, 这里可以通过快慢指针的方式找到中点:
    1. 快指针每次移动两步, 慢指针每次移动一步
    2. 这样快指针变成空的时候, 慢指针就到了中点, 即左半部分末尾
    3. 慢指针的下个节点就是右半部分开头, 保存它, 并断开慢指针和它的连接
  2. 然后对右半部分进行翻转, 这里可以使用双指针做到, 具体可以参考之前的文章: [024 TODO]
  3. 最后再将两部分交替合并即可, 这里同样使用双指针来进行合并:
    1. 保存左右指针的下个节点
    2. 左指针指向右指针
    3. 右指针指向左指针下个节点
    4. 左右指针移动到先前保存的各自下个节点的位置
  • 下面代码有详细的注释, 方便大家理解
复杂度
  • 时间复杂度 O(N): 每步操作都只需要遍历每个节点一次
  • 空间复杂度 O(1): 只使用了几个常数空间的变量
代码
class Solution:
    def reorderList(self, head: ListNode) -> None:
        """
        Do not return anything, modify head in-place instead.
        """
        # 快慢指针找中点+右半部分翻转+链表合并
        if not head:
            return None
        # 第一步: 快慢指针找中点
        # 注意长度是偶数的话中点使用左半部分最后一个节点
        fast = head.next
        slow = head
        while fast and fast.next:
            slow = slow.next
            fast = fast.next.next
        # 第二步: 右半部分翻转
        # 此时slow是左半部分的尾节点
        # 先断开左右部分的连接
        cur = slow.next
        slow.next = None
        # 然后进行经典链表翻转
        pre = None
        while cur:
            nex = cur.next
            cur.next = pre
            pre, cur = cur, nex
        # 第三步: 链表合并
        # 此时pre是右半部分翻转后的头节点
        # 使用双指针依次指向对方下一个节点即可
        l, r = head, pre
        while l and r:
            nl = l.next
            nr = r.next
            l.next = r
            r.next = nl
            l = nl
            r = nr
        return head

大家可以在下面这些地方找到我~😊

我的 GitHub

我的 Leetcode

我的 CSDN

我的知乎专栏

我的头条号

我的牛客网博客

我的公众号: 算法精选, 欢迎大家扫码关注~😊

算法精选 - 微信扫一扫关注我

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值