[前端]_一起刷leetcode 143. 重排链表

539 阅读3分钟

大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。

题目

143. 重排链表

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

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 * 104]
  • 1 <= node.val <= 1000

思路

首先,如果不明白怎么翻转一条链表的,可以先从入门看起 [路飞]_送给前端开发者的链表入门教程

  1. 如果是普通的数组,咱们可以直接采取双指针,左一个,右一个,左一个,右一个...,这道题目就太简单了哈哈;
  2. 由于是链表,我们要拿到最后一个节点,所以我们首先得经过一轮遍历,拿到翻转后的链表,同时计算链表的长度count
  3. 这样子我们有了一条正序排列的链表和一条反序排列的链表了,每一个从正的取一个,反的取一个,就可以实现我们刚刚的左一个,右一个,左一个,右一个了;
  4. 如果链表的单数的,最后一轮要在左边在拿一个,然后记得切割掉链表最后一个节点的next的指向。

实现

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {void} Do not return anything, modify head in-place instead.
 */
var reorderList = function(head) {
    // 下一轮还需要头节点,找个代跑
    let temp = head;

    
    // 计算链表的长度
    let count = 0;
    
    // 生成一个倒序的链表
    let reverseHead = null;
    while (temp) {
        let newNode = new ListNode(temp.val);
        newNode.next = reverseHead;
        reverseHead = newNode;
        temp = temp.next;
        count++;
    }

    let result = new ListNode(0);
    let prev = result;

    // 快乐的从左边拿一个,右边拿一个
    while (count > 1) {
        // 左一个
        prev.next = head;
        prev = prev.next;
        head = head.next;

        // 右一个
        prev.next = reverseHead;
        prev = prev.next;
        reverseHead = reverseHead.next;

        count -= 2;
    }

    // 单数的情况下,最后一轮只能左一个了
    if (count === 1) {
        // 左一个
        prev.next = head;
        prev = prev.next;
    }

    prev.next = null;

    return result.next;
};

结果

image.png

翻车了。超出了输出限制,说明空间上用多了, 可以考虑从reverseHead下手,我们其实用不到前半截,可以直接拿后半截来做一个翻转,也可以从result下手,我们直接操作原链表,不用新开辟内存空间。这里我们选择用前者。

优化

通过快慢指针找到中点处,不用翻转整个链表,切断中点,翻转后半截链表即可,然后左右两边各取一个值进行操作。

优化代码

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * @param {ListNode} head
 * @return {void} Do not return anything, modify head in-place instead.
 */
var reorderList = function(head) {
    // 通过快慢指针找到中点处的位置
    let fast = head, slow = head;
    let count = 0;
    while (fast) {
        fast = fast.next && fast.next.next;
        slow = slow.next;
        count++;
    }

    // 生成一个倒序的链表
    let reverseHead = null;
    while (slow) {
        let newNode = new ListNode(slow.val);
        newNode.next = reverseHead;
        reverseHead = newNode;
        slow = slow.next;
    }

    let result = new ListNode(0);
    let prev = result;

    while (count) {
        // 左一个
        prev.next = head;
        prev = prev.next;
        head = head.next;

        // 右一个
        prev.next = reverseHead;
        prev = prev.next;
        reverseHead = reverseHead && reverseHead.next;

        count--;
    }

    return result.next;
};

看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。