/** * https://leetcode.cn/problems/copy-list-with-random-pointer/?envType=study-plan-v2&envId=top-interview-150 * // Definition for a _Node. * function _Node(val, next, random) { * this.val = val; * this.next = next; * this.random = random; * }; */ /** * @param {_Node} head * @return {_Node} */ const copyRandomList = function (head) { }; // Definition for a _Node. // eslint-disable-next-line no-underscore-dangle function _Node(val, next, random) { this.val = val; this.next = next; this.random = random; } /* 遍历整个链表,创建每一个节点的克隆节点,并且使用map将它们直接联系起来,原节点作为key,新节点作为value, 遍历完毕之后,再遍历一一遍原链表,根据random_index和map来为可能列表添加random_index */ function f1(head) { const map = new Map(); // 储存原链表和克隆链表直接的关系 const dummyHead = new _Node(); // 哑节点,用于构建克隆链表 let curClone = dummyHead; let cur = head; while (cur) { // 创建克隆节点 curClone.next = new _Node(cur.val); curClone = curClone.next; // 将原节点和克隆节点映射 map.set(cur, curClone); cur = cur.next; } // 遍历原节点,根据原节点的信息为克隆节点添加random指针 cur = head; curClone = dummyHead.next; while (cur) { if (cur.random) { curClone.random = map.get(cur.random); } cur = cur.next; curClone = curClone.next; } return dummyHead.next; } /* f1 的优化版本,首先创建克隆节点和映射,之后再拼接克隆链表 */ function f2(head) { if (!head) return null; const map = new Map(); // 储存原节点 => 克隆节点的映射 let cur = head; // 第一次遍历:创建所有新节点,并放入 map 中 while (cur) { map.set(cur, new _Node(cur.val)); cur = cur.next; } cur = head; // 第二次遍历:设置新节点的 next 和 random 指针 while (cur) { const cloneNode = map.get(cur); cloneNode.next = map.get(cur.next) || null; cloneNode.random = map.get(cur.random) || null; cur = cur.next; } return map.get(head); } /* 直接再原节点中插入克隆节点,那么原节点和克隆节点就存在了引用关系,之后根据引用关系来设置random,最后 分离原链表和克隆链表即可 */ function f3(head) { if (!head) return null; // 创建克隆节点,并且把克隆节点放在原节点后面 let cur = head; while (cur) { // 将原节点之后的所有节点移动到克隆节点的后面,再将克隆节点作为原节点的next const clone = new _Node(cur.val); clone.next = cur.next; cur.next = clone; cur = clone.next; // 遍历下一个原节点 } // 更具原节点的random来设置克隆节点的random cur = head; while (cur) { if (cur.random) { cur.next.random = cur.random.next; } cur = cur.next.next; // 下一个原节点 } // 分离节点 cur = head; const cloneHead = head.next; while (cur) { const clone = cur.next; cur.next = clone.next; // 原节点指向原本下一个节点 if (clone.next) { clone.next = clone.next.next; // 克隆节点指向克隆节点的下一个节点 } cur = cur.next; } return cloneHead; } /* 回溯加哈希表 */ function f4(head, cacheMap = new Map()) { if (!head) return null; // 如果没有这个节点的克隆映射 if (!cacheMap.has(head)) { const clone = new _Node(head.val); cacheMap.set(head, clone); // 克隆节点的下一个节点为当前节点的下一个节点的克隆节点,递归调用即可 clone.next = f4(head.next, cacheMap); // random同理 clone.random = f4(head.random, cacheMap); } // 如果有克隆节点,直接返回 return cacheMap.get(head); }