“Offer 驾到,掘友接招!我正在参与2022春招打卡活动,点击查看活动详情。”
一、题目描述
给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln - 1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。
示例 1:
输入:head = [1,2,3,4]
输出:[1,4,2,3]
示例 2:
输入:head = [1,2,3,4,5]
输出:[1,5,2,4,3]
提示:
- 链表的长度范围为
[1, 5 * 104] 1 <= node.val <= 1000
二、思路分析
题目要求重排链表,规则为 L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → … ,以 head = [1,2,3,4,5] 为例,重排的链表为 1->5->2->4->3 ,也就是说要“反复横跳”。
这可为难了,链表又不像数组,想获取哪个元素就获取哪个元素,要是链表能变成数组就好了。
- 解法一:存储
这么一想,好像有思路了,我们可以把链表存进元素为ListNode的数组中,这样就可以随时随地操作链表了。说干就干,思路如下:
- 遍历链表,将遍历到的节点存进数组
vec中。 - 遍历结束之后,定义双指针,一个
i在数组最前面,一个j在数组最后面。当i < j时,不断修改链表指向,vec[i++] -> next = vec[j]、vec[j--] -> next = vec[i]。
注意:当
i == j时,就不用继续修改链表了,此时vec[i]已经是链表的末尾了。
- 解法二:寻找链表中点 + 链表逆序 + 合并链表 我们不难发现,所谓“反复横跳”,就是链表从最前面指向最后面、最后面指向次前面、次前面指向次后面...因此,我们可以
- ①将链表分成两半(方便“反复横跳”)。使用快慢,就是指针,当快指针走到末尾时,返回慢指针,即为链表中点。
- ②反转后半部分的链表(方便最前面指向最后面)。反转流程可参考我之前写过的题解:。
- ③合并 前半部分链表 和 反转后的后半部分链表。前半部分的链表在前,轮流交替修改链表指向。 举个栗子直观展示:
要重排的链表为:1 -> 2 -> 3 -> 4 -> 5 -> 6
第一步,将链表平均分成两半
1 -> 2 -> 3
4 -> 5 -> 6
第二步,将第二个链表逆序
1 -> 2 -> 3
6 -> 5 -> 4
第三步,依次连接两个链表
1 -> 6 -> 2 -> 5 -> 3 -> 4
三、AC 代码
链表定义如下:
/**
* 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:
void reorderList(ListNode *head) {
if (head == nullptr) {
return;
}
vector<ListNode *> vec;
ListNode *node = head;
// 将 head 的每个节点存进数组中
while (node != nullptr) {
vec.emplace_back(node);
node = node->next;
}
int i = 0, j = vec.size() - 1;
while (i < j) {
vec[i] -> next = vec[j];
i++;
if (i == j) {
break;
}
vec[j] -> next = vec[i];
j--;
}
vec[i]->next = nullptr; // 重排后的链表的末尾要指向 nullptr
}
};
解法二:寻找链表中点 + 链表逆序 + 合并链表
class Solution {
public:
void reorderList(ListNode* head) {
if (!head) {
return;
}
ListNode* mid = middleNode(head);
ListNode* L = head;
ListNode* R = mid -> next;
R = reverseList(R);
mid -> next = nullptr; // 重排后的链表的末尾要指向 nullptr
mergeList(L, R);
}
ListNode* middleNode(ListNode* head) {
ListNode* slow = head, * fast = head;
while (fast -> next && fast -> next -> next) {
slow = slow -> next;
fast = fast -> next -> next;
}
return slow;
}
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* cur = head;
while (cur) {
ListNode* after = cur -> next;
cur -> next = pre;
pre = cur;
cur = after;
}
return pre;
}
void mergeList(ListNode* L, ListNode* R) {
ListNode* l1;
ListNode* l2;
while (L != nullptr && R != nullptr) {
// 保存两个链表的后一个节点,方便后移
l1 = L -> next;
l2 = R -> next;
L -> next = R;
L = l1;
R -> next = L;
R = l2;
}
}
};