/** * // Definition for a _Node. * function _Node(val, neighbors) { * this.val = val === undefined ? 0 : val; * this.neighbors = neighbors === undefined ? [] : neighbors; * }; * https://leetcode.cn/problems/clone-graph/?envType=study-plan-v2&envId=top-interview-150 */ /** * @param {_Node} node * @return {_Node} */ const cloneGraph = function (node) { }; // eslint-disable-next-line no-underscore-dangle, no-unused-vars function _Node(val, neighbors) { this.val = val === undefined ? 0 : val; this.neighbors = neighbors === undefined ? [] : neighbors; } /* 只给了我们一个连通图的一个节点,叫我们克隆整个图,根据两图图的定义,从这个节点出发我们能到达途中的任意一个节点(顶点), 所以我们只需要遍历所有的neighbor,然后顺着邻居继续遍历邻居即可遍历整个图,但是这里有一个问题,就是如果通过邻居到达了 自己,或者邻居的邻居,是自己的邻居,就会发生循环,这里就遇到使用一种数据结果来缓存已经遍历过的邻居,当再次遇到它时跳过就行。 抽象过程:你需要统计小王家人,你只认识小王,于是题通过小王认识了它的爸爸,妈妈,把他们加入到统计表,然后小王的爸爸向你介绍了 它的妹妹,同时小王向你介绍了它的姑姑,但是你发现这两个人是同一个人,所以你只统计了一次,最后你就获得了所有和小王有关的人。 */ function f1(node) { const visited = new Map(); // 储存原节点,和克隆节点的映射 /** * * @param {*} node 需要克隆的节点 * @returns 返回克隆的节点 */ function clone(node) { if (!node) return node; // 如果这个节点之前克隆过,直接返回其克隆节点 if (visited.has(node)) return visited.get(node); // 创建这个节点的克隆节点 const cloneNode = new _Node(node.val, []); // node与cloneNode建立映射 visited.set(node, cloneNode); // 为克隆节点克隆邻居节点 for (const neighbor of node.neighbors) { cloneNode.neighbors.push(clone(neighbor)); } return cloneNode; } return clone(node); } /* 思路和上面一致但是使用广度优先遍历 */ function f2(node) { if (!node) return node; const visited = new Map(); const queue = [node]; // 用于广度遍历 const cNode = new _Node(node.val, []); visited.set(node, cNode); // 克隆第一个节点,将其存入visited while (queue.length > 0) { const n = queue.shift(); // 遍历这个节点的所有邻居,如果没有访问过将其clone,并加入visited for (const neighbor of n.neighbors) { if (!visited.has(neighbor)) { visited.set(neighbor, new _Node(neighbor.val, [])); queue.push(neighbor); } visited.get(n).neighbors.push(visited.get(neighbor)); } } return cNode; }