328. Odd Even Linked List

練習日期:2026-02-06

難度:Medium

類型:Linked List

📘 題目敘述

給定一個單向鏈結串列 head,請將所有位於奇數位置的節點放在前面,所有位於偶數位置的節點放在後面,並回傳重新排列後的鏈結串列。

這裡的「奇數 / 偶數」指的是節點在鏈結串列中的位置(1-indexed),而不是節點的值。

請注意:奇數節點與偶數節點內部的相對順序要保持不變。

條件限制

  • 節點數量範圍為 [0, 10^4]
  • -10^6 ≤ Node.val ≤ 10^6

🧠 解題思路(一)建立dummy

我的做法是「分兩次掃描原本的 linked list」,把結果依序接到一條新的 linked list 上:

  1. 先走奇數位置head (第1個) -> 第3個 -> 第5個 -> ...
  2. 再走偶數位置head->next (第2個) -> 第4個 -> 第6個 -> ...

每走到一個節點,我就把它的值 valnew ListNode(...) 建一個新節點,接到結果串列的尾端。

這樣做可以很直觀地達到:

  • 奇數位置全部先接完
  • 偶數位置再接在後面
  • 並且保持原本的相對順序

所有變數

  • head:輸入鏈結串列的頭節點
  • dummy:結果鏈結串列的虛擬頭(方便接節點,不用特判第一個)
  • tail:結果鏈結串列的尾巴指標(每次都把新節點接在尾巴後面)
  • h:用來掃描原鏈結串列的指標(第一次掃奇數位置,第二次掃偶數位置)

🪜 主要流程步驟

1. 前置判斷:長度太短直接回傳

if (!head || !(head->next)) {
    return head;
}
  • 如果 head == nullptr:空串列。
  • head->next == nullptr:只有一個節點。這兩種情況本來就已經符合題意,不需要重排。

2. 建立結果串列的起點(dummy + tail)

ListNode dummy(0);
ListNode* tail = &dummy;
  • dummy 是一個不重要的節點(值是 0 只是佔位)。
  • 真正答案會從 dummy.next 開始。
  • tail 一開始指向 dummy,代表結果串列目前是空的。

3. 第一段 while:掃「奇數位置」節點並接到結果

ListNode* h = head;
while (h) {
    tail->next = new ListNode(h->val);
    tail = tail->next;
    if (h->next) {
        h = h->next->next;
    } else {
        break;
    }
}

這段的意思是:

  • hhead 開始(第 1 個節點,奇數位置)。
  • 每次把 h->val 複製成新節點接到結果尾端。
  • 然後 h 嘗試跳兩步 h = h->next->next,這樣就會走到下一個奇數位置(1→3→5→...)。

為什麼要 if (h->next)

  • 因為要跳兩步之前,至少要確保 h->next 存在。
  • 否則 h->next->next 會出錯。

4. 第二段 while:掃「偶數位置」節點並接到結果尾端

h = head->next;
while (h) {
    tail->next = new ListNode(h->val);
    tail = tail->next;
    if (h->next) {
        h = h->next->next;
    } else {
        break;
    }
}

這段跟上一段完全一樣,只是起點變成 head->next(第 2 個節點,偶數位置)。

所以會走:第2個 → 第4個 → 第6個 → ...,最後就把偶數位置節點全部接在奇數位置後面。


5. 回傳答案

return dummy.next;

因為 dummy 只是虛擬頭,真正串列的頭是 dummy.next

💻 程式碼實作(一)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        if (!head || !(head->next)) {
            return head;
        }
        ListNode dummy(0);
        ListNode* tail = &dummy;
        ListNode* h = head;
        while (h) {
            tail->next = new ListNode(h->val);
            tail = tail->next;
            if (h->next) {
                h = h->next->next;
            } else {
                break;
            }
        }
        h = head->next;
        while (h) {
            tail->next = new ListNode(h->val);
            tail = tail->next;
            if (h->next) {
                h = h->next->next;
            } else {
                break;
            }
        }
        return dummy.next;
    }
};

🧠 解題思路(二)奇偶指標

我這份寫法是「把原串列拆成兩條鏈,再接起來」,全程只改 next 指標,不建新節點。

核心想法:

  • odd 指向目前「奇數鏈」最後一個節點。
  • even 指向目前「偶數鏈」最後一個節點。
  • ehead 保留偶數鏈的頭(原本的第二個節點),最後要把它接回奇數鏈尾端。

重點是:每次都把下一個奇數節點接到 odd 後面,再把下一個偶數節點接到 even 後面,這樣相對順序就會自然保持原樣。

所有變數

  • head:輸入 linked list 的頭節點,也是最後回傳的頭。
  • odd:目前奇數鏈的尾巴指標(一路往後推進)。
  • even:目前偶數鏈的尾巴指標(一路往後推進)。
  • ehead:偶數鏈的頭指標(用來在最後接回去)。

🪜 主要流程步驟

1. 特例:長度 0 或 1 直接回傳

  • 如果 !head!head->next,代表節點不夠分奇偶,原樣回傳即可。

2. 初始化奇偶兩條鏈的起點

  • odd = head:第一個節點(奇數)。
  • even = head->next:第二個節點(偶數)。
  • ehead = even:記住偶數鏈的頭,最後要接回去。

3. 交替重接指標,把奇數節點串成一條、偶數節點串成一條

迴圈條件用 while (even && even->next),因為要把「下一個奇數節點」接到 odd 後面,這個節點就是 even->next,所以 even->next 必須存在。

每一輪做兩次接法:

  1. 接奇數鏈:odd->next = even->next,再 odd = odd->next(odd 往前走到新的尾巴)。
  2. 接偶數鏈:even->next = odd->next,再 even = even->next(even 往前走到新的尾巴)。

4. 把偶數鏈接到奇數鏈尾端

  • 迴圈結束後,奇數鏈已經在前面串好了。
  • odd->next = ehead 把偶數鏈整條接到後面。

5. 回傳 head

  • 因為是原地改指標,頭不變,回傳 head

💻 程式碼實作(二)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* oddEvenList(ListNode* head) {
        if (!head || !head->next) {
            return head;
        }
        ListNode* odd = head;        // 奇數
        ListNode* even = head->next; // 偶數
        ListNode* ehead = even;
        while (even && even->next) {
            odd->next = even->next;
            odd = odd->next;
            even->next = odd->next;
            even = even->next;
        }
        odd->next = ehead;
        return head;
    }
};

https://leetcode.com/problems/odd-even-linked-list/

作者: scottnick
撰寫日期: 2026-02-06
類別:
原文連結: https://scottnick.github.io/cpp-notes/leetcode75/328-odd-even-linked-list.html