写在前面
本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更……
专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容进行回顾与总结,文章结构大致如下,部分内容会有增删:
- Tag:介绍本题牵涉到的知识点、数据结构;
- 题目来源:贴上题目的链接,方便大家查找题目并完成练习;
- 题目解读:复述题目(确保自己真的理解题目意思),并强调一些题目重点信息;
- 解题思路:介绍一些解题思路,每种解题思路包括思路讲解、实现代码以及复杂度分析;
- 知识回忆:针对今天介绍的题目中的重点内容、数据结构进行回顾总结。
Tag
【图】【深搜】【广搜】
题目来源
解题思路
深拷贝和浅拷贝是 C++ 内存管理中十分重要的知识,通俗的讲,浅拷贝指向同一块内存地址,而深拷贝指向的是不同的内存地址。浅拷贝之后,修改内存地址另一个对象也会被修改,而深拷贝则不会。
对于本题而言,需要对图进行深拷贝,图的深拷贝即构建一张与原图结构、节点值均一样的图,但是其中的节点指向的并不原图的节点。我们需要重新构造节点与结构。
由于题目只给了我们一个节点的引用,因此为了知道整张图的结构以及对应节点的值,我们需要从给定的节点出发,进行「图的遍历」,并在遍历的过程中完成图的深拷贝。
对于「图的遍历」可以通过深搜实现也可以通过广搜实现。
因为本题中的图是无向图,因而无论是深搜还是广搜都要避免在遍历图时陷入死循环,可以使用哈希表来避免这种现象发生,哈希表中键为已经遍历到的节点,其值为克隆的节点。
方法一:深搜
代码
/*
// 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 {
private:
unordered_map<Node*, Node*> visited;
public:
// 每次调用返回的是 node 节点的克隆节点
Node* cloneGraph(Node* node) {
if (!node) {
return nullptr;
}
// 如果该节点已经被访问过了,直接从哈希表中取出对应的克隆节点返回
if (visited.find(node) != visited.end()) {
return visited[node];
}
// 克隆节点并记录到哈希表中
Node* cloneNode = new Node(node->val);
visited[node] = cloneNode;
// 遍历该节点的邻居节点并更新克隆节点的邻居节点
for (auto& nb : node->neighbors) {
cloneNode->neighbors.emplace_back(cloneGraph(nb));
}
return cloneNode;
}
};
复杂度分析
时间复杂度: O ( n ) O(n) O(n), n n n 表示节点数量。深度优先搜索遍历图的过程中每个节点只会被访问一次。
空间复杂度: O ( n ) O(n) O(n)。哈希表占用的空间为 O ( n ) O(n) O(n),递归调用栈最坏的空间复杂度也为 O ( n ) O(n) O(n)。
方法二:广搜
代码
class Solution {
public:
Node* cloneGraph(Node* node) {
if (!node) { // 特判
return node;
}
unordered_map<Node*, Node*> visited;
queue<Node*> que;
que.push(node);
visited[node] = new Node(node->val);
while (!que.empty()) {
Node* cur = que.front(); que.pop();
// 遍历邻居节点
for (auto& nb : cur->neighbors) {
// 如果没有被访问(克隆)过,就克隆并存储
if (visited.find(nb) == visited.end()) {
visited[nb] = new Node(nb->val);
que.push(nb);
}
// 更新当前节点的邻居
visited[cur]->neighbors.emplace_back(visited[nb]);
}
}
return visited[node];
}
};
复杂度分析
时间复杂度: O ( n ) O(n) O(n), n n n 表示节点数量。深度优先搜索遍历图的过程中每个节点只会被访问一次。
空间复杂度: O ( n ) O(n) O(n)。哈希表占用的空间为 O ( n ) O(n) O(n),广搜中的队列在最坏情况下的空间复杂度也为 O ( n ) O(n) O(n)。
写在最后
如果您发现文章有任何错误或者对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。
如果大家有更优的时间、空间复杂度的方法,欢迎评论区交流。
最后,感谢您的阅读,如果有所收获的话可以给我点一个 👍 哦。