题目描述
假设你有一个单向链表 L
,其首节点被标为 head
,这个链表代表了小美的工作任务流程:
L0 → L1 → … → Ln-1 → Ln
你需要对其进行重新组织,以达到以下新的工作任务流程:
L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …
请注意,这里不能只修改节点任务的内容,而是需要实际地进行节点任务的交换。
示例 1:
输入:L = 1 → 2 → 3 → 4 → 5
输出:1 → 5 → 2 → 4 → 3
核心思路
为了解决这个问题,我们需要重新组织单向链表,使其满足特定的顺序:第一个节点后跟最后一个节点,然后第二个节点,接着倒数第二个节点,以此类推。关键在于实际交换节点,而不仅仅是修改节点值。
- 寻找链表中点:使用快慢指针技术找到链表的中间节点。慢指针每次移动一步,快指针每次移动两步。当快指针到达末尾时,慢指针指向链表的中间节点。
- 分割链表:将链表从中间节点分割成两个部分。前半部分从头部到中间节点,后半部分从中间节点的下一个节点开始。
- 反转后半部分:将后半部分链表反转,以便能够从末尾开始遍历。
- 合并链表:将前半部分链表和反转后的后半部分链表交替合并,形成所需的顺序。
代码实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
|
class ListNode { int val; ListNode next;
ListNode() {
}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; } }
class Solution {
public void reorderList(ListNode head) { if (head == null || head.next == null) { return; } ListNode slow = head; ListNode fast = head.next; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; } ListNode second = slow.next; slow.next = null; second = reverseList(second); ListNode first = head; while (first != null && second != null) { ListNode firstNext = first.next; ListNode secondNext = second.next; first.next = second; second.next = firstNext; first = firstNext; second = secondNext; } }
private ListNode reverseList(ListNode head) { ListNode prev = null; ListNode curr = head; while (curr != null) { ListNode nextTemp = curr.next; curr.next = prev; prev = curr; curr = nextTemp; } return prev; } }
|
算法示例
- 假设原始链表为:
1 → 2 → 3 → 4 → 5
- 找到中点:节点
3
- 分割链表:前半部分
1 → 2 → 3
,后半部分 4 → 5
- 反转后半部分:
5 → 4
- 合并链表:
1 → 5 → 2 → 4 → 3
- 最终结果为:
1 → 5 → 2 → 4 → 3
复杂度分析
- 时间复杂度:
O(n)
。寻找链表中点:O(n/2) ≈ O(n)
,反转后半部分链表:O(n/2) ≈ O(n)
,合并两个链表:O(n/2) ≈ O(n)
,总体时间复杂度为线性 O(n)
。
- 空间复杂度:
O(1)
。算法只使用了常数级别的额外空间(几个指针变量)。