原题链接: 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 函数稍有变化, 不同之处在于, 如果有两个中间节点, 则返回第一个中间节点.
具体步骤如下.
1. 找到原链表的中间节点
ListNode* mid = middleNode(head);
2. 将原链表平均切割成两段
ListNode* l1 = head, * l2 = mid->next;
mid->next = nullptr; 将原链表断成两部分.
3. 反转切割下来的后半段链表
l2 = reverseList(l2);
4. 合并两段链表 l1, l2
mergeList(l1, l2);
三. 复杂度分析
时间复杂度: 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
}
};