[路飞]_重排链表

137 阅读2分钟

题目介绍

给定一个单链表 L 的头节点 head ,单链表 L 表示为:

L0L1 → … → Ln - 1Ln

请将其重新排列后变为:

L0LnL1Ln - 1L2Ln - 2 → …

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

示例1

image.png

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

示例2

image.png

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

提示:

解题思路

思路一

由于链表没有下标,我们不方便对链表的节点进行操作,因此先将所有的节点保存到数组中,通过数组下标来将链表重排

  1. 先将链表的节点全部存到数组中
  2. 定义两个指针 i、j 分别从数组的前后开始遍历
  3. 将 j 指向的节点插入到 i 节点和其下一个节点之间
  4. i + 1, j - 1
  5. 重复 3-4,直到 i >= j

7.gif

解题代码

var reorderList = function(head) {
    const arr = []
    let p = head
    // 为了使用节点的下标,将节点都保存到数组中
    while (p) {
        arr.push(p)
        p = p.next
    }
    let i = 0; j = arr.length - 1
    while (i < j) {
        arr[i].next = arr[j]
        i++
        // 当 i === j 时,说明 j 是 i 的下一位,不需要再进行交换
        if (i === j) {
            break
        }
        arr[j].next = arr[i]
        j--
    }
    // 需要将尾节点的 next 指向 null,否则会形成环形链表
    arr[i].next = null
    return arr[0]
};

思路二:链表翻转

可以将链表的后半段进行翻转,然后将翻转之后的新链表从头节点开始依次插入到原链表中

  1. 定义一个链表翻转的方法
  2. 定义快慢指针 pq 分别指向头节点和头节点的下一个节点
  3. 每次 p 指针走一步, q 指针走两步
  4. q 指针指向空或者 q 指针的下一个节点为空时,此时 p 节点指向待翻转链表的前一个节点
  5. p 指针之后的链表进行翻转,p 指针的下一个节点指向空
  6. 将翻转后的链表从头节点开始依次间隔插入到原链表中
  7. 返回原链表的头节点

8.gif

解题代码

var reorderList = function(head) {
    // 如果链表为空或者只有一个节点,不需要重排
    if (!head || !head.next) return head
    // 定义快慢指针
    let p = head, q = head.next
    // 当 q 走到 null 或者链表最后一个节点时,p 走到链表中点
    while (q && q.next) {
        p = p.next
        q = q.next.next
    }
    // 将链表的后半段翻转
    const newList = reverse(p.next)
    p.next = null
    p = head
    q = newList
    // 将两个链表拼接成一个
    while (q) {
        const q_next = q.next
        q.next = p.next
        p.next = q
        p = p.next.next
        q = q_next
    }
    return head
};
// 翻转链表
var reverse = function (head) {
    let p = head, next = p.next
    while (head.next) {
        head.next = head.next.next
        next.next = p
        p = next
        next = head.next
    }
    return p
}