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 ) 也就得以正确复制。

直观的解释:

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

相关推荐
Swift社区2 小时前
LeetCode - #139 单词拆分
算法·leetcode·职场和发展
Kent_J_Truman3 小时前
greater<>() 、less<>()及运算符 < 重载在排序和堆中的使用
算法
IT 青年3 小时前
数据结构 (1)基本概念和术语
数据结构·算法
熬夜学编程的小王3 小时前
【初阶数据结构篇】双向链表的实现(赋源码)
数据结构·c++·链表·双向链表
Dong雨3 小时前
力扣hot100-->栈/单调栈
算法·leetcode·职场和发展
SoraLuna4 小时前
「Mac玩转仓颉内测版24」基础篇4 - 浮点类型详解
开发语言·算法·macos·cangjie
liujjjiyun4 小时前
小R的随机播放顺序
数据结构·c++·算法
¥ 多多¥4 小时前
c++中mystring运算符重载
开发语言·c++·算法
trueEve5 小时前
SQL,力扣题目1369,获取最近第二次的活动
算法·leetcode·职场和发展
天若有情6735 小时前
c++框架设计展示---提高开发效率!
java·c++·算法