0. Basic Concepts
0.1 When to use BFS?
-
Traversal in Graph
- Level Order Traversal
- Connected Component
- Topological Sorting
-
Shortest Path in Simple Graph
Limited to the simple graph, which means no direction, edge length is 1.
1. Problem Type:
Tips:
- Use Hash Map/Set wisely
- If we could use BFS, avoid using DFS. It’s more clear and easy.
1.1 Level Traverse in Binary Tree
Specific content was mentioned in 4.Binary Tree(With sample problem).
1.2 HashMap
In some problem, we hash map/set is good tool for us to solve the problem. For example, if we want to check there is a circle in graph.
Also, classic clone graph problem as second example here. We will use hash map to store the relation between the old node and new node.
1.2.1 Examples:
Solve the problem by using unordered_set and queue. The main idea is BFS, each time we visit a new node, put it into vectice and queue.
bool validTree(int n, vector<pair<int, int>>& edges) {
vector<unordered_set<int>> g(n, unordered_set<int>());
unordered_set<int> v;
queue<int> q;
q.push(0);
v.insert(0);
for (auto a : edges) {
g[a.first].insert(a.second);
g[a.second].insert(a.first);
}
while (!q.empty()) {
int t = q.front(); q.pop();
for (auto a : g[t]) {
if (v.find(a) != v.end()) return false;
v.insert(a);
q.push(a);
g[a].erase(t);
}
}
return v.size() == n;
}
As the problem required, we need to deep copy the graph. Thus, we will use queue to bfs the graph, and hash map to store the relation between old node and new node.
Two solution below, but the same idea here.
Solution1:
- BFS the graph, meanwhile, copy the node and store into hashmap.
- Iterate the original nodes set, fill up the neighbors
UndirectedGraphNode* cloneGraph(UndirectedGraphNode* node) {
// write your code here
if(!node)
return node;
unordered_map<int, UndirectedGraphNode* > hash;
queue<UndirectedGraphNode*> Q;
unordered_set<UndirectedGraphNode*> set;
set.insert(node);
Q.push(node);
while(! Q.empty())
{
UndirectedGraphNode* ptr = Q.front(); Q.pop();
if(hash.find(ptr->label) == hash.end())
{
UndirectedGraphNode* tmp = new UndirectedGraphNode(ptr->label);
hash[ptr->label] = tmp;
}
for(auto now : ptr->neighbors)
{
if(set.find(now) == set.end())
{
set.insert(now);
Q.push(now);
}
}
}
for(auto now : set)
{
UndirectedGraphNode* ptr = hash[now->label];
for(auto neighbor : now->neighbors)
{
ptr->neighbors.push_back(hash[neighbor->label]);
}
}
return hash[node->label];
}
Solution2:
- Use hash map to store the old node and new node. Every time it visits a new node, fill its neighbors immediately.
UndirectedGraphNode* cloneGraph(UndirectedGraphNode* node) {
// write your code here
if(!node){
return node;
}
// initialization
UndirectedGraphNode* p1 = node;
UndirectedGraphNode* p2 = new UndirectedGraphNode(node ->label);
unordered_map<UndirectedGraphNode*, UndirectedGraphNode*> old2new;
queue<UndirectedGraphNode*> queue;
// queue used to record the orginal node
queue.push(node);
// old2new is a hash to record the connection of old node and new one
old2new[node] = p2;
// while there existed nodes remained to be visited
while(! queue.empty()){
p1 = queue.front(); p2 = old2new[p1];
queue.pop();
vector<UndirectedGraphNode *> neighbors = p1->neighbors;
for(int i = 0; i < neighbors.size(); i++){
// if neighbor node is new node
if(old2new.find(neighbors[i]) == old2new.end()){
UndirectedGraphNode* tmp = new UndirectedGraphNode(neighbors[i]->label);
old2new[neighbors[i]] = tmp;
p2->neighbors.push_back(tmp)