9.15 BFS中等 133 Clone Graph review 138 随机链表的复制

133 Clone Graph

cpp 复制代码
//错误代码

class Solution {
public:
    Node* cloneGraph(Node* node) {
        //邻接表、BFS---》类似于二叉树的层次遍历
        if(!node || !node->val) return node;
        //构造队列
        queue<Node*> prev;
        prev.push(node);
        //构造新的图结点列表
        vector<Node*> adjList;
        //辅助数组
        // 哈希集合用于记录访问过的节点
        unordered_set<Node*> visited;
        visited.insert(node);
        while(prev.empty()){
            //获取结点并出栈
            Node* newNode = new Node();
            Node* curr = prev.front();prev.pop();

            newNode->val = curr->val;
            for(Node* neighbor : curr->neighbors){
                //复制对应的邻接点
                newNode->neighbors.push_back(neighbor);
                // 如果邻居没有被访问过
                if (visited.find(neighbor) == visited.end()) {
                    prev.push(neighbor);         // 将未访问的邻居加入队列
                    visited.insert(neighbor); // 标记为已访问
                }
            }
            adjList.push_back(newNode);
        }
        return adjList[0];
    }
};)
  • 在克隆图的过程中,直接使用了原图中的邻居列表(newNode->neighbors.push_back(neighbor)),而没有克隆邻居节点的副本。实际上,需要复制整个图中的节点及其连接关系 ,因此应为每个节点创建新的副本,而不是直接使用原图的邻居节点
  • 图的克隆和链表的复制(特别是带随机指针的链表的深度复制138) 很类似。两者都涉及节点的复制,并且不仅仅是单独复制节点本身,还必须考虑其邻接点或其他关联节点(例如,图的邻居节点或链表的随机指针)。
cpp 复制代码
/*
// Definition for a Node.
class Node {
public:
    int val;
    vector<Node*> neighbors;
    Node() {
        val = 0;
        neighbors = vector<Node*>();
    }
    Node(int _val) {
        val = _val;
        neighbors = vector<Node*>();
    }
    Node(int _val, vector<Node*> _neighbors) {
        val = _val;
        neighbors = _neighbors;
    }
};
*/

class Solution {
public:
    Node* cloneGraph(Node* node) {
        if(!node) return nullptr;
        //原结点-->克隆结点
        unordered_map<Node* , Node*> cloneNodes;
        //BFS
        queue<Node*> prev;
        prev.push(node);
        //第一个克隆结点
        cloneNodes[node] = new Node(node->val);

        while(!prev.empty()){
            Node* curr = prev.front();
            prev.pop();

            for(Node* neighbor : curr->neighbors){
                //如果邻居结点还没有被克隆
                if(cloneNodes.find(neighbor) == cloneNodes.end()){
                    cloneNodes[neighbor] = new Node(neighbor->val);
                    //加入队列
                    prev.push(neighbor);
                }
                //将克隆的邻居节点加入当前克隆节点的邻接表
                cloneNodes[curr]->neighbors.push_back(cloneNodes[neighbor]);
            }
        }
        return cloneNodes[node];

    }
};

138 随机链表的复制

cpp 复制代码
/*
// Definition for a Node.
class Node {
public:
    int val;
    Node* next;
    Node* random;
    
    Node(int _val) {
        val = _val;
        next = NULL;
        random = NULL;
    }
};
*/

class Solution {
public:
    Node* copyRandomList(Node* head) {
        if(!head) return nullptr;
        //建立新链表
        Node* p = head;             //p是原链表指针
        Node* dum = new Node(0);
        Node* temp = dum;           //temp是新链表指针

        //构造哈希表
        unordered_map<Node* , Node*> copyList;
        //第一次遍历原链表,复制next指针
        while(p){
            Node* node = new Node(p->val);
            temp->next = node;

            copyList[p] = node;

            temp = temp->next;
            p = p->next;
        }
        //第二次遍历原链表,复制random
        p = head;
        temp = dum->next;
        while(p){
            if(p->random != nullptr){
                temp->random = copyList[p->random];
            }
            temp = temp->next;
            p = p->next;
        }
        return dum->next;


    }
};

summary 哈希表与克隆:用于存储原节点和新节点的映射

1. 防止重复克隆

当我们遍历图或链表时,如果不使用哈希表,就可能会多次创建同一个节点的副本,从而浪费内存和时间。哈希表通过记录原节点和对应的新节点,确保每个节点只会被克隆一次。每当需要访问一个节点的邻居(在图中)或者 nextrandom(在链表中)时,哈希表可以直接提供已克隆的节点,而无需重复创建。

2. 保证边的复制

当你遍历一个节点时,你不仅需要复制该节点,还需要复制它所有的边(即图中的邻接点或者链表中的指针)。这里哈希表起到了关键作用:

  • 对于图的克隆,当你克隆一个节点时,它的邻居节点可能已经被克隆,也可能还没有。如果没有克隆,哈希表会创建并存储这个邻居节点的克隆版;如果已经克隆,你可以通过哈希表找到对应的克隆节点,并将它加入当前节点的邻接表中,确保边的复制。


  • 对于链表,类似地,你需要通过哈希表将 randomnext 指针正确指向克隆后的节点,而不是指向原链表中的节点。


3. 流程示例

以图为例,当我们遍历到一个节点 ( A ) 时,如果节点 ( A ) 的邻居 ( B ) 还未被克隆,哈希表就会克隆 ( B ) 并将其存储。接下来,无论你何时再次遇到 ( B ),都可以直接从哈希表中获取克隆节点。这样,边 ( A - B ) 也就得以正确复制。

直观的解释:

通过哈希表的存储和查找机制,可以保证每次访问节点时,邻居的指向都是已经克隆的副本,而不是原图中的节点,从而保证边(指针)的完整性

相关推荐
程序猿进阶2 小时前
如何在 Visual Studio Code 中反编译具有正确行号的 Java 类?
java·ide·vscode·算法·面试·职场和发展·架构
Eloudy2 小时前
一个编写最快,运行很慢的 cuda gemm kernel, 占位 kernel
算法
king_machine design2 小时前
matlab中如何进行强制类型转换
数据结构·算法·matlab
西北大程序猿2 小时前
C++ (进阶) ─── 多态
算法
无名之逆2 小时前
云原生(Cloud Native)
开发语言·c++·算法·云原生·面试·职场和发展·大学期末
头发尚存的猿小二2 小时前
树——数据结构
数据结构·算法
好蛊2 小时前
第 2 课 春晓——cout 语句
c++·算法
山顶夕景2 小时前
【Leetcode152】分割回文串(回溯 | 递归)
算法·深度优先·回溯
紫钺-高山仰止3 小时前
【Matlab】matlab 结构体使用方法
数据结构·算法·matlab
夜幕龙3 小时前
robomimic基础教程(三)——自带算法
人工智能·python·算法·机器人