Swift 数据结构与算法(24) 链表 + M_Leetcode 328. 奇偶链表(双指针)

99 阅读6分钟

概念

知识点记忆

问题:

var odd = head, even = head?.next, evenHead = even ,

为什么无论怎么操作, evenHead 就都不会变? 能代表全部的 even, 不能使用 even 直接代表吗? 然后, 为什么, odd 就没有这个问题?

回答

链表是一系列节点,每个节点都有一个指向下一个节点的指针。当我们移动一个指针(例如 oddeven),我们实际上只是改变了指针的值,而不是链表本身。

  1. 为什么 evenHead 不会变,能代表全部的 even?

    在此代码中,我们使用 evenHead 来存储偶数节点的头部。因为在我们的操作中,even 指针会不断地向后移动,指向偶数位置的下一个节点。但是,为了最后能将奇数节点的尾部连接到偶数节点的头部,我们需要知道偶数节点的起始位置。因此,我们使用 evenHead 来保持这个起始位置不变,即使 even 指针已经移动了。

  2. 为什么不能直接使用 even 代表全部的偶数节点?

    如上所述,even 是一个移动的指针,它会随着我们的操作不断地向后移动。如果我们没有 evenHead 来保存偶数节点的起始位置,那么在操作结束时,我们将不知道偶数节点的起始位置在哪里,从而无法将奇数节点的尾部正确地连接到它上。

  3. 为什么 odd 没有这个问题?

    这是因为我们的返回值是整个链表的头部,即 head。在这个特定的问题中,head 始终是奇数节点的起始位置。odd 指针会移动,但是当我们需要返回整个链表的起始位置时,我们直接返回 head,不需要另一个额外的指针来存储它。

/ even 的下一个节点应该是 odd 的下一个节点,即下一个偶数节点 even?.next = odd?.next 我还是不太理解这个?

考虑链表:1,2,3,41,2,3,4

这里:

  • 1 是奇数位置上的节点
  • 2 是偶数位置上的节点
  • 3 是奇数位置上的节点
  • 4 是偶数位置上的节点

初始化时:

  • odd 指向 1
  • even 指向 2

首先,odd 的下一个应该指向 even 的下一个,即 3。所以现在 1 的下一个是 3,链表看起来是:1,3,2,41,3,2,4。但是 2 的下一个仍然是 3,我们需要更新它。

现在,让我们看这行代码 even?.next = odd?.next

在上面的步骤中,我们已经将 odd 更新为指向 3。因此,odd?.next 现在是 4。所以我们正在更新 even 的下一个节点为 4。

因此,经过这两步操作后,链表变成了:1,3,2,41,3,2,4,并且 odd 指向 3,而 even 指向 2。

回到 even?.next = odd?.next 这行代码,它的目的是确保 even 节点(或偶数位置的节点)总是连接到下一个偶数位置的节点。这是为了保证奇数位置的节点和偶数位置的节点在调整后仍然是分开的,并且它们之间没有交叉。

题目

328. 奇偶链表 给定单链表的头节点 head ,将所有索引为奇数的节点和索引为偶数的节点分别组合在一起,然后返回重新排序的列表。

第一个节点的索引被认为是 奇数 , 第二个节点的索引为 偶数 ,以此类推。

请注意,偶数组和奇数组内部的相对顺序应该与输入时保持一致。

你必须在 O(1) 的额外空间复杂度和 O(n) 的时间复杂度下解决这个问题。

 

示例 1:

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

示例 2:

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

 

提示:

  • n ==  链表中的节点数
  • 0 <= n <= 104
  • -106 <= Node.val <= 106
class Solution {
    func oddEvenList(_ head: ListNode?) -> ListNode? {
         
    }
}

解题思路🙋🏻‍ ♀️

题目分析:

这道题要求我们按照奇偶索引来重新组织一个链表。具体地说,我们需要将所有奇数位置的节点放在一起,然后是所有偶数位置的节点。并要求在O(1)的额外空间复杂度和O(n)的时间复杂度下完成。

回答问题:

我们的函数需要返回一个链表的头节点,这个链表是重新排列后的链表。

题目类型:

这是一道链表操作题。

解题思路:

  1. 初始化两个指针 oddeven 来表示当前的奇数节点和偶数节点。同时,保留 even 的头部,因为在最后我们需要连接奇数部分和偶数部分。
  2. 遍历链表,odd 指针总是跳到下一个奇数节点,而 even 指针跳到下一个偶数节点。这样,我们就可以分别得到奇数部分和偶数部分。
  3. 当遍历完成后,我们将 odd 的下一个指针指向 even 的头部,这样就连接了两个部分。
  4. 返回新的头节点。

整体上,我们可以将这个过程想象成两个人在赛跑,一个人每次跑两步(even),另一个人每次跑一步(odd)。这样,跑得快的人就可以找到所有的偶数节点,而跑得慢的人则可以找到所有的奇数节点。

边界思考🤔

代码

class Solution {
    func oddEvenList(_ head: ListNode?) -> ListNode? {
        // 如果头节点为空或只有一个节点,直接返回头节点
        if head == nil || head?.next == nil {
            return head
        }
        
        // 创建奇数节点的头和当前节点
        var odd = head
        // 创建偶数节点的头和当前节点
        var even = head?.next
        // 保存偶数节点的头,以便之后将奇数节点和偶数节点连接起来
        let evenHead = even

        // 当 odd 和 even 都存在时,执行循环
        while even != nil && even?.next != nil {
            // odd 的下一个节点应该是 even 的下一个节点,即下一个奇数节点
            odd?.next = even?.next
            // 更新 odd 到下一个奇数节点
            odd = odd?.next
            
            // even 的下一个节点应该是 odd 的下一个节点,即下一个偶数节点
            even?.next = odd?.next
            // 更新 even 到下一个偶数节点
            even = even?.next
        }
        
        // 最后,将奇数节点的最后一个节点与偶数节点的头节点连接起来
        odd?.next = evenHead
        
        return head
    }
}

时空复杂度分析

O (n)

引用

本系列文章部分概念内容引用 www.hello-algo.com/

解题思路参考了 abuladong 的算法小抄, 代码随想录... 等等

Youtube 博主: huahua 酱, 山景城一姐,