Leetcode 143. 重排链表

293 阅读2分钟

原题链接: 143. 重排链表 - 力扣(Leetcode)

tag: 双指针, 链表.

在阅读本文前, 请先阅读如下三篇题解.

Leetcode 876. 链表的中间结点 - 掘金 (juejin.cn)

Leetcode 206. 反转链表 - 掘金 (juejin.cn)

Leetcode 21. 合并两个有序链表 - 掘金 (juejin.cn)

一. 题目

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

L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为:

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

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

二. 题解

本题需要复用两个之前做过的题目的函数, middleNode 和 reverseList .

其中 middleNode 函数稍有变化, 不同之处在于, 如果有两个中间节点, 则返回第一个中间节点.

image.png

具体步骤如下.

1. 找到原链表的中间节点

ListNode* mid = middleNode(head);

image.png

2. 将原链表平均切割成两段

ListNode* l1 = head, * l2 = mid->next;

image.png

mid->next = nullptr; 将原链表断成两部分.

image.png

3. 反转切割下来的后半段链表

l2 = reverseList(l2);

image.png

4. 合并两段链表 l1, l2

mergeList(l1, l2);

image.png

三. 复杂度分析

时间复杂度: O(N), N 是链表的节点个数.

空间复杂度: O(1).

四. 代码

/**
 * 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* middleNode(ListNode* head) {
        ListNode* slow = head, * fast = head;    // 定义慢指针 slow, 快指针 fast
        while (fast->next && fast->next->next) {
            slow = slow->next;    // 慢指针走一步
            fast = fast->next->next;    // 快指针走两步
        }
        return slow;    // 返回链表的中间节点
    }

    ListNode* reverseList(ListNode* head) {
        ListNode* prev = nullptr, * curr = head;    // 定义前指针 prev, 中指针 curr
        while (curr != nullptr) {
            ListNode* temp = curr->next;    // 保存 curr 的后继节点
            curr->next = prev;    // 进行反转操作
            prev = curr;    // 更新 prev 指针
            curr = temp;    // 更新 curr 指针
        }
        return prev;    // 返回新的头节点
    }

    void mergeList(ListNode* l1, ListNode* l2) {
        while (l1 && l2) {
            ListNode* l1_temp = l1->next;    // 记录 l1 的下一个节点
            ListNode* l2_temp = l2->next;    // 记录 l2 的下一个节点

            l1->next = l2;    // 改变 l1 节点 next 指针的朝向
            l1 = l1_temp;    // 更新 l1 指针

            l2->next = l1;    // 改变 l2 节点 next 指针的朝向
            l2 = l2_temp;    // 更新 l2 指针
        }
    }

    void reorderList(ListNode* head) {
        ListNode* mid = middleNode(head);    // 找到原链表的中间节点
        ListNode* l1 = head, * l2 = mid->next;    
        mid->next = nullptr;    // 将原链表平均切割成两段

        l2 = reverseList(l2);    // 反转切割下来的后半段链表

        mergeList(l1, l2);    // 合并两段链表 l1, l2
    }
};