返回

JavaScript重排链表——轻松搞定LeetCode 143

前端

重排链表:一步步掌握 JavaScript 算法

序言

在软件开发领域,解决算法问题是至关重要的。LeetCode 143 题“重排链表”就是一道经典的算法题,考验我们的链表操作能力。本文将深入探讨这道题目的 JavaScript 解决方案,并提供详细的步骤和示例代码。

算法概述

重排链表算法的核心思想是将链表分成两半,然后将后半部分的节点插入前半部分的节点之间。具体来说,我们可以将其分解为以下步骤:

  1. 链表分割: 将链表从中间分割为两半,前半部分长度为 n/2,后半部分长度为 n/2(或 n/2+1,如果链表长度为奇数)。
  2. 后半部分反转: 将后半部分的链表反转,以便节点顺序与前半部分相反。
  3. 合并链表: 将反转的后半部分链表插入前半部分链表的节点之间,形成一个新的重排链表。

代码示例

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
/**
 * Given a singly linked list L: L0→L1→…→Ln-1→Ln,
 * reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→…
 *
 * You may not modify the values in the list's nodes. Only nodes themselves may be changed.
 *
 * Example 1:
 *
 * Given 1->2->3->4, reorder it to 1->4->2->3.
 * Example 2:
 *
 * Given 1->2->3->4->5, reorder it to 1->5->2->4->3.
 *
 * @param {ListNode} head
 * @return {void} Do not return anything, modify head in-place instead.
 */
const reorderList = function(head) {
  if (!head || !head.next) {
    return;
  }

  // Find the middle of the linked list.
  let slow = head;
  let fast = head.next;
  while (fast && fast.next) {
    slow = slow.next;
    fast = fast.next.next;
  }

  // Split the linked list into two halves.
  const secondHalf = slow.next;
  slow.next = null;

  // Reverse the second half of the linked list.
  let prev = null;
  let current = secondHalf;
  while (current) {
    const next = current.next;
    current.next = prev;
    prev = current;
    current = next;
  }

  // Merge the two halves of the linked list.
  let firstHalf = head;
  let secondHalfReversed = prev;
  while (secondHalfReversed) {
    const next1 = firstHalf.next;
    const next2 = secondHalfReversed.next;
    firstHalf.next = secondHalfReversed;
    secondHalfReversed.next = next1;
    firstHalf = next1;
    secondHalfReversed = next2;
  }
};

算法复杂度

  • 时间复杂度: O(n),其中 n 是链表的长度。
  • 空间复杂度: O(1),因为我们没有使用额外的空间。

常见问题解答

  1. 如何找到链表的中间节点?

    使用快慢指针方法。快指针每次移动两步,慢指针每次移动一步。当快指针到达链表末尾时,慢指针正好指向中间节点。

  2. 如何反转链表?

    使用迭代方法。将当前节点的 next 指针指向前一个节点,然后将前一个节点更新为当前节点。重复这个过程,直到到达链表末尾。

  3. 如何将两个链表合并?

    迭代地遍历两个链表。对于每个节点,将后一个链表的第一个节点插入到前一个链表的当前节点的 next 指针之后。然后,更新后一个链表的第一个节点为其 next 指针。

  4. 如果链表的长度是奇数怎么办?

    在这种情况下,后半部分的链表长度将比前半部分多一个节点。在合并链表时,将额外的节点插入到前半部分的最后一个节点之后。

  5. 如何测试算法?

    创建具有不同长度和元素的链表,然后使用算法对它们进行重排。验证结果是否与预期的重排顺序一致。

总结

重排链表算法是链表操作中一个重要的概念。通过理解算法的步骤和实现细节,我们可以解决各种算法问题。本文提供了 JavaScript 算法的全面概述,包括示例代码、算法复杂度和常见问题解答。通过练习和理解,我们可以掌握链表操作技能,成为更熟练的软件开发人员。