28. 重排链表【LC143】

139 阅读1分钟

题目:

给定一个单链表 L 的头节点 head ,单链表 L 表示为: L0 → L1 → … → Ln - 1 → Ln

请将其重新排列后变为: L0 → Ln → L1 → Ln - 1 → L2 → Ln - 2 → …

不能只是单纯的改变节点内部的值,而是需要实际的进行节点交换。

image.png

核心思路:

方法一: 快慢指针

  1. 先利用快慢指针,找到后半段链表,后半段链表需要反转。 无论总共奇数个节点还是偶数个节点,当2n+1超出链表总长时,慢指针所指的位置的下一个节点一定是后半段链表。
  // [8] 1 2 3 4 5 6 7 8
  // 1 3 5 7
  // 1 2 3 4
  // mid 4
  // 1 2 3 4 & 5 6 7 8

  // ====================
  // [7] 1 2 3 4 5 6 7
  // 1 3 5 7
  // 1 2 3 4
  // mid 4
  // 1 2 3 4 & 5 6 7

2.对后半段链表进行反转

3.对两段链表进行合并,要注意,链表1的长度预定长于链表2,以此为循环条件。

方法二,双指针法:【空间换时间】

  • 将链表每个节点断开,放入数组s
  • 指针i从头向尾,指针j从尾向头,重新组装链表

解:

// 方法一:快慢指针
var reorderList = function (head) {
  if (!head || !head.next) {
    return head;
  }
  let slow = head;
  let fast = head;
  while (fast && fast.next && fast.next.next) {
    fast = fast.next.next;
    slow = slow.next;
  }
  let mid = slow;
  let l1 = head;
  let l2 = mid.next; //中间节点后段,无论单双数
  mid.next = null;
  //反转后半段
  let revHead = null;
  let p = null;
  while (l2) {
    p = l2;
    l2 = l2.next;
    p.next = revHead;
    revHead = p
  }
  const res = {};
  p = res;
  while (l1) {

    //另种可能,一种l1比l2长,另一种一样长
    p.next = l1;
    l1 = l1.next;
    p = p.next;
    if (revHead) {
      p.next = revHead;
      revHead = revHead.next;
      p = p.next
    }
  }
  return res.next;
};

方法二:双指针
var reorderList = function(head, s = [], tmp) {// 换行可删除,合并到4行
    while (head) 
        tmp = head.next, 
        head.next = null, 
        s.push(head), 
        head = tmp
    var i = -1, j = s.length
    while (++i < --j) 
        s[i].next = s[j], 
        j !== i + 1 && (s[j].next = s[i + 1])
    return s[0] 
};