描述

给定一个单链表 L 的头节点 head ,单链表 L 表示为:
L0 → L1 → … → Ln-1 → Ln
请将其重新排列后变为:
L0 → Ln → L1 → Ln-1 → L2 → Ln-2 → …
不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

示例

剑指 Offer II 026. 重排链表 - 图1

  1. 输入: head = [1,2,3,4]
  2. 输出: [1,4,2,3]

剑指 Offer II 026. 重排链表 - 图2

  1. 输入: head = [1,2,3,4,5]
  2. 输出: [1,5,2,4,3]

提示

  • 链表的长度范围为 [1, 5 * 104]
  • 1 <= node.val <= 1000

解题思路

线性表

用线性表存储该链表,然后利用线性表可以下标访问的特点,直接按顺序访问指定元素,重建该链表即可。

代码

  1. class Solution {
  2. public void reorderList(ListNode head) {
  3. if (head == null) {
  4. return;
  5. }
  6. List<ListNode> list = new ArrayList<ListNode>();
  7. ListNode node = head;
  8. while (node != null) {
  9. list.add(node);
  10. node = node.next;
  11. }
  12. int i = 0, j = list.size() - 1;
  13. while (i < j) {
  14. list.get(i).next = list.get(j);
  15. i++;
  16. if (i == j) {
  17. break;
  18. }
  19. list.get(j).next = list.get(i);
  20. j--;
  21. }
  22. list.get(i).next = null;
  23. }
  24. }

复杂度分析

  • 时间复杂度:O(N), 其中 N 是链表中的节点数。
  • 空间复杂度:O(N), 其中 N 是链表中的节点数。主要为线性表的开销。

寻找链表中点 + 链表逆序 + 合并链表

  1. 找到原链表的中点
    • 我们可以使用快慢指针来 O(N) 地找到链表的中间节点。
  2. 将原链表的右半端反转
    • 我们可以使用迭代法实现链表的反转。
  3. 将原链表的两端合并。
    • 因为两链表长度相差不超过 1,因此直接合并即可。

代码

  1. /**
  2. * Definition for singly-linked list.
  3. * public class ListNode {
  4. * int val;
  5. * ListNode next;
  6. * ListNode() {}
  7. * ListNode(int val) { this.val = val; }
  8. * ListNode(int val, ListNode next) { this.val = val; this.next = next; }
  9. * }
  10. */
  11. class Solution {
  12. public void reorderList(ListNode head) {
  13. if (head == null) return;
  14. ListNode mid = findMidNode(head);
  15. ListNode l1 = head;
  16. ListNode l2 = mid.next;
  17. mid.next = null;
  18. l2 = revertList(l2);
  19. merge(l1, l2);
  20. }
  21. public ListNode findMidNode(ListNode head) {
  22. if (head == null) return null;
  23. ListNode slow = head;
  24. ListNode fast = head;
  25. while (fast.next != null && fast.next.next != null) {
  26. slow = slow.next;
  27. fast = fast.next.next;
  28. }
  29. return slow;
  30. }
  31. public ListNode revertList(ListNode head) {
  32. if (head == null) return null;
  33. ListNode pre = null;
  34. ListNode curr = head;
  35. while (curr != null) {
  36. ListNode next = curr.next;
  37. curr.next = pre;
  38. pre = curr;
  39. curr = next;
  40. }
  41. return pre;
  42. }
  43. public void merge(ListNode l1, ListNode l2) {
  44. ListNode l1_tmp;
  45. ListNode l2_tmp;
  46. while (l1 != null && l2 != null) {
  47. l1_tmp = l1.next;
  48. l2_tmp = l2.next;
  49. l1.next = l2;
  50. l1 = l1_tmp;
  51. l2.next = l1;
  52. l2 = l2_tmp;
  53. }
  54. }
  55. }

复杂度分析

  • 时间复杂度:O(N),其中 N 是链表中的节点数。
  • 空间复杂度:O(1)。