Medium
题目描述
给你一个单链表的头节点 head,链表如下所示:
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 * 10^4]
- 1 <= Node.val <= 1000
解题思路
这道题要求将链表重排为交替取头尾节点的形式。我们可以用几种方法来解决:
方法1:栈辅助(空间复杂度O(n)) 遍历链表将所有节点压入栈中,然后交替从头部和栈顶取节点重新连接。
方法2:数组存储(空间复杂度O(n)) 将所有节点存入数组,利用双指针从两端向中间移动重新连接节点。
方法3:三步法(推荐,空间复杂度O(1)) 这是最优解法,分为三个步骤:
- 找到中点:使用快慢指针找到链表的中点
- 反转后半部分:将后半部分链表反转
- 合并两个链表:将前半部分和反转后的后半部分交替合并
这种方法的优势是只需要O(1)的额外空间,且思路清晰。通过快慢指针技巧找中点是链表问题的经典套路,反转链表和合并链表也是基础操作。整个过程保持了原地操作的要求。
代码实现
class Solution {
public:
void reorderList(ListNode* head) {
if (!head || !head->next) return;
// 1. 找到中点
ListNode* slow = head;
ListNode* fast = head;
ListNode* prev = nullptr;
while (fast && fast->next) {
prev = slow;
slow = slow->next;
fast = fast->next->next;
}
// 断开链表
prev->next = nullptr;
// 2. 反转后半部分
ListNode* second = reverse(slow);
// 3. 合并两个链表
ListNode* first = head;
while (second) {
ListNode* temp1 = first->next;
ListNode* temp2 = second->next;
first->next = second;
second->next = temp1;
first = temp1;
second = temp2;
}
}
private:
ListNode* reverse(ListNode* head) {
ListNode* prev = nullptr;
ListNode* curr = head;
while (curr) {
ListNode* next = curr->next;
curr->next = prev;
prev = curr;
curr = next;
}
return prev;
}
};
class Solution:
def reorderList(self, head: Optional[ListNode]) -> None:
if not head or not head.next:
return
# 1. 找到中点
slow = fast = head
prev = None
while fast and fast.next:
prev = slow
slow = slow.next
fast = fast.next.next
# 断开链表
prev.next = None
# 2. 反转后半部分
second = self.reverse(slow)
# 3. 合并两个链表
first = head
while second:
temp1, temp2 = first.next, second.next
first.next = second
second.next = temp1
first, second = temp1, temp2
def reverse(self, head):
prev = None
curr = head
while curr:
next_node = curr.next
curr.next = prev
prev = curr
curr = next_node
return prev
public class Solution {
public void ReorderList(ListNode head) {
if (head == null || head.next == null) return;
// 1. 找到中点
ListNode slow = head, fast = head;
ListNode prev = null;
while (fast != null && fast.next != null) {
prev = slow;
slow = slow.next;
fast = fast.next.next;
}
// 断开链表
prev.next = null;
// 2. 反转后半部分
ListNode second = Reverse(slow);
// 3. 合并两个链表
ListNode first = head;
while (second != null) {
ListNode temp1 = first.next;
ListNode temp2 = second.next;
first.next = second;
second.next = temp1;
first = temp1;
second = temp2;
}
}
private ListNode Reverse(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
}
var reorderList = function(head) {
if (!head || !head.next) return;
// 1. 找到中点
let slow = head, fast = head;
let prev = null;
while (fast && fast.next) {
prev = slow;
slow = slow.next;
fast = fast.next.next;
}
// 断开链表
prev.next = null;
// 2. 反转后半部分
let second = reverse(slow);
// 3. 合并两个链表
let first = head;
while (second) {
let temp1 = first.next;
let temp2 = second.next;
first.next = second;
second.next = temp1;
first = temp1;
second = temp2;
}
};
function reverse(head) {
let prev = null;
let curr = head;
while (curr) {
let next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 |
|---|---|---|
| 栈辅助 | O(n) | O(n) |
| 数组存储 | O(n) | O(n) |
| 三步法(推荐) | O(n) | O(1) |
三步法是最优解:
- 时间复杂度:O(n),需要遍历链表三次(找中点、反转、合并),每次都是O(n)
- 空间复杂度:O(1),只使用了常数个额外变量,没有使用额外的数据结构