leetcode
leetcode 301 ~ 350
奇偶链表

奇偶链表

难度:

标签:

题目描述

给定单链表的头节点 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

代码结果

运行时间: 56 ms, 内存: 16.9 MB


/*
 * 思路:
 * 1. 由于Java Stream不适用于直接操作链表结构,我们可以将链表转换为列表,然后使用stream操作。
 * 2. 创建两个列表分别存储奇数索引和偶数索引的节点。
 * 3. 遍历原始链表,将节点按索引加入对应的列表。
 * 4. 合并奇数和偶数列表。
 * 5. 将合并后的列表转换回链表。
 */
 
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
 
class ListNode {
    int val;
    ListNode next;
    ListNode(int x) { val = x; }
}
 
public class Solution {
    public ListNode oddEvenList(ListNode head) {
        if (head == null) return null;
        List<ListNode> nodes = new ArrayList<>();
        while (head != null) {
            nodes.add(head);
            head = head.next;
        }
        List<ListNode> oddNodes = nodes.stream()
                .filter(node -> nodes.indexOf(node) % 2 == 0)
                .collect(Collectors.toList());
        List<ListNode> evenNodes = nodes.stream()
                .filter(node -> nodes.indexOf(node) % 2 != 0)
                .collect(Collectors.toList());
        List<ListNode> resultNodes = new ArrayList<>();
        resultNodes.addAll(oddNodes);
        resultNodes.addAll(evenNodes);
        for (int i = 0; i < resultNodes.size() - 1; i++) {
            resultNodes.get(i).next = resultNodes.get(i + 1);
        }
        resultNodes.get(resultNodes.size() - 1).next = null;
        return resultNodes.get(0);
    }
}

解释

方法:

本题解的思路是通过维护两个指针,分别指向奇数位置和偶数位置的节点,然后将偶数位置的节点逐个摘除并接到另一个链表上。最后,将两个链表连接起来。这样可以在一次遍历中完成奇偶分离。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
在定义`dummy_left`和`dummy_right`时,它们的作用和必要性是什么?
`dummy_left`节点作为假的头节点,主要用于简化对链表头部的处理,尤其是在头部节点也可能被操作或变更时,可以避免复杂的边界条件判断。通过始终通过`dummy_left.next`来访问真正的头节点,即使头节点变化,也不需要额外的判断或更新。`dummy_right`则用于临时存储被摘除的偶数位置节点,它同样作为一个假的头节点,帮助轻松地在其后添加节点,并在处理完毕后容易地获取新的偶数节点链表的头部。
🦆
为什么在处理偶数位置的节点时,需要将`cur.next`设置为`None`?这一步是否会影响链表的完整性?
在将偶数位置的节点`cur`移动到偶数链表中时,需要将`cur.next`设置为`None`以断开原来的连接,防止新的偶数链表与原链表形成环或其他不正常的链接。这一步是必要的,因为接下来`cur`将会被接到`dummy_right`表示的偶数链表的末尾。这一操作不会影响原链表的完整性,因为在摘除`cur`节点之前,已经将`cur`的前一个节点`pre`的`next`指向`cur.next`,即跳过了`cur`,保持了链表其余部分的连续性。
🦆
在将偶数链表接到奇数链表末尾前,为什么不需要检查`dummy_right.next`是否为`None`?这是否可能导致结果链表中出现断链?
实际上,检查`dummy_right.next`是否为`None`是一个好习惯,因为如果原链表全部是奇数节点,那么`dummy_right.next`将会是`None`,直接连接会导致奇数链表的末尾接上一个空节点。虽然在给定的代码中没有这一检查,但最好在连接前添加这样的判断以避免不必要的空节点引用。若不检查,不会造成断链,但会有不必要的空引用。
🦆
循环中使用`odd`变量来控制奇偶位置,这种方式是否有可能在特定情况下导致错误的奇偶节点分离,例如链表节点数量的奇偶性不同时?
`odd`变量确实用于控制当前节点是奇数还是偶数位置,通过在每次循环中交替其值来实现。这种方法本质上是稳定的,因为它依赖于节点的遍历顺序,而与链表的总长度奇偶性无关。无论链表的长度是奇数还是偶数,`odd`的初始值和其在循环中的变化逻辑确保了节点正确地被识别为奇数位置或偶数位置。因此,这种方法不会因为链表长度的奇偶性不同而导致错误的分离。

相关问题

分隔链表

给你一个头结点为 head 的单链表和一个整数 k ,请你设计一个算法将链表分隔为 k 个连续的部分。

每部分的长度应该尽可能的相等:任意两部分的长度差距不能超过 1 。这可能会导致有些部分为 null 。

k 个部分应该按照在链表中出现的顺序排列,并且排在前面的部分的长度应该大于或等于排在后面的长度。

返回一个由上述 k 部分组成的数组。

 

示例 1:

输入:head = [1,2,3], k = 5
输出:[[1],[2],[3],[],[]]
解释:
第一个元素 output[0] 为 output[0].val = 1 ,output[0].next = null 。
最后一个元素 output[4] 为 null ,但它作为 ListNode 的字符串表示是 [] 。

示例 2:

输入:head = [1,2,3,4,5,6,7,8,9,10], k = 3
输出:[[1,2,3,4],[5,6,7],[8,9,10]]
解释:
输入被分成了几个连续的部分,并且每部分的长度相差不超过 1 。前面部分的长度大于等于后面部分的长度。

 

提示:

  • 链表中节点的数目在范围 [0, 1000]
  • 0 <= Node.val <= 1000
  • 1 <= k <= 50