[路飞]复杂链表的复制

145 阅读2分钟

题目描述

请实现 copyRandomList 函数,复制一个复杂链表。在复杂链表中,每个节点除了有一个 next 指针指向下一个节点,还有一个 random 指针指向链表中的任意节点或者 null。

分析

输入:链表头节点 head
输出:复制的链表的头节点

解题思路

这道题有两种解法:

哈希表 + 回溯

我们这么思考,如果是一个普通的链表,只需要从头到尾地遍历一遍,创建一个新链表,设置上复制的值就可以了,但是由于我们的链表节点有一个 random 属性,所以我们需要通过回溯的方法创建节点。

首先我们用一个 map 记录下来每个节点的创建情况,从头开始复制节点。

对于每个节点,我们先创建它,复制 val,并保存在 map,然后我们递归地进行 nextrandom 的复制。这样每次递归过程中,如果 map 里有这个节点,我们就直接返回它。

迭代 + 节点拆分

我们也可以通过迭代的方法去省掉哈希的空间,首先我们从头到尾遍历链表,没遍历到一个节点,我们做一次复制,声明为 newNode 作为当前遍历到节点的 next。注意,第一次遍历的时候可以先不复制 random,只复制出所有的节点即可。

第二次遍历我们把所有新复制出来的节点的 random 指向正确的位置。

第三次遍历我们做节点拆分,把复制的节点组成新的链表,返回它的 head

代码

/**
 * @param {Node} head
 * @return {Node}
 */
var copyRandomList = function (head, map = new Map()) {
  if (head === null) return null;

  if (!map.has(head)) {
    map.set(head, {
      val: head.val,
    });

    // take care that we should firstly create one node before we go deeper, or there never gonna be one node created
    Object.assign(map.get(head), {
      next: copyRandomList(head.next, map),
      random: copyRandomList(head.random, map),
    });
  }

  return map.get(head);
};

/**
 * // Definition for a Node.
 * function Node(val, next, random) {
 *    this.val = val;
 *    this.next = next;
 *    this.random = random;
 * };
 */

/**
 * @param {Node} head
 * @return {Node}
 */
var copyRandomList = function (head) {
  if (!head) return null;

  // copy node val
  for (let node = head; node !== null; node = node.next.next) {
    const newNode = new Node(node.val, node.next, null);
    node.next = newNode;
  }

  // copy node random
  for (let node = head; node !== null; node = node.next.next) {
    const newNode = node.next;
    newNode.random = node.random && node.random.next;
  }

  let newHead = head.next;
  // unlink
  for (let node = head; node !== null; node = node.next) {
    const newNode = node.next;
    node.next = node.next.next;
    newNode.next = (newNode.next && newNode.next.next) || null;
  }

  return newHead;
};

复杂度

时间:O(N),遍历所有节点几遍
空间:O(N) for 哈希,O(1) for 迭代