Leetcode中有几道题涉及到图的遍历,即 BFS 和 DFS,两道题都是深度遍历 DFS超时,而 BFS被 AC。这应该不是偶然!
第一道题:Word Ladder
Given two words (start and end), and a dictionary, find the length of shortest transformation sequence fromstart toend, such that:
1. only one letter can be changed at a time
2. Each intermediate word must exist in the dictionary
Given: start="hit", end="cog", dict=["hot", "dot", "dog", "lot", "log"], one shortest transformation is "hit-> hot->dot->dog->cog"
思路:宽度优先遍历,利用queue分别记下遍历的字符串(word)和该字符串的跳数(step);另外 1)遍历'a'-'z'这26个字符,产生下一跳候选集合;其实我一开始并没有想到这个方法,原因是有两层循环。但是如果通过不同的查字典,每一个去对比,同样需要两种循环,如果字典很大,或者字符串很长,则复杂度较于前者更高。2)利用step去记录访问的字符串的跳数也是我没想到的(如果可以的话,可以利用这个标记实现按层次(跳数)遍历图);最先到达终点的即为最短路径。3)图的遍历少不了做标记 visited.
class Solution {
public:
int ladderLength(string start, string end, unordered_set<string> &dict) {
unordered_set<string> visited;
queue<string> q;
queue<int> step;
q.push(start);
visited.insert(start);
step.push(1);
while(!q.empty()){
string word=q.front();
int st=step.front();
q.pop();
step.pop();
for(int i=0; i<word.length(); i++){
char saved=word[i];
for(char c='a'; c<='z'; c++){
word[i]=c;
if(word==end) return st+1;
if(dict.count(word) && visited.count(word)==0){
q.push(word);
visited.insert(word);
step.push(st+1);
}
}
word[i]=saved;
}
}
return 0;
}
};
很重要的一点:想要加速程序运行速度,visited.insert必须紧接着push操作,而不是pop!
第二道题:Word Ladder II
Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that:
1 . Only one letter can be changed at a time.
2. Each intermediate word must exist in a dictionary
思路:依旧使用宽度优先遍历算法。这里是要找出所有最短路径。
第三道题:Surround Regions
这是一道典型的图的遍历题。也是DFS不断超时,原因嘛,有人是重复分值太多 !最后改用BFS通过。
思路:这道题不像前面那道题,思路也很直接。找candidates就是在判断当前点的四个方向。貌似这道题没有重复遍历的判断,比较特殊,可以把#理解成标记吧。
class Solution {
public:
void traverseBoard(vector<vector<char> > &board, int i, int j){
int M=board.size();
int N=board[0].size();
if(i<0 || j<0 || i==M || j==N)
return;
queue<int> q;
q.push(i*N+j);
board[i][j]='#';
while(!q.empty()){
int val=q.front();
q.pop();
i=val/N;
j=val%N;
if(i>0 && board[i-1][j]=='O') { q.push((i-1)*N+j); board[i-1][j]='#';}
if(i<M-1 && board[i+1][j]=='O'){ q.push((i+1)*N+j);board[i+1][j]='#';}
if(j>0 && board[i][j-1]=='O'){ q.push(i*N+j-1);board[i][j-1]='#';}
if(j<N-1 && board[i][j+1]=='O'){ q.push(i*N+j+1);board[i][j+1]='#';}
}
}
void solve(vector<vector<char> > &board) {
if(board.size()==0) return;
int M=board.size();
int N=board[0].size();
for(int i=0; i<M; i++){
for(int j=0; j<N; j++){
if(board[i][j]=='O' && (i==0 || j==0 || i==M-1 || j==N-1)){
traverseBoard(board, i, j);
}
}
}
for (int i = 0; i <M; ++i)
for (int j = 0; j<N; ++j)
board[i][j] = (board[i][j] == '#') ? 'O' : 'X';
}
};
第四道题:Clone Graph
Given an undirected graph. Each node in the graph contains a label and a list of neighbors.
思路:这道题用一般都BFS或者DFS都可以解,但是要用设计好visit数组,这里使用map<int, UndirectedGraphNode*> 将label和节点的地址关联起来,这样通过label就可以得到该节点。
/**
* Definition for undirected graph.
* struct UndirectedGraphNode {
* int label;
* vector<UndirectedGraphNode *> neighbors;
* UndirectedGraphNode(int x) : label(x) {};
* };
*/
class Solution {
public:
UndirectedGraphNode *cloneGraph(UndirectedGraphNode *node) {
if(node==NULL) return NULL;
map<int, UndirectedGraphNode *> visited;
return doCopyGraphNode(node,visited);
}
UndirectedGraphNode *doCopyGraphNode(UndirectedGraphNode *node, map<int, UndirectedGraphNode *>& visited){
UndirectedGraphNode* cnode=new UndirectedGraphNode(node->label);
visited[node->label]=cnode;
vector<UndirectedGraphNode *>::iterator it=(node->neighbors).begin();
for(;it!=(node->neighbors).end();it++){
//如果邻居节点已经被访问过了,则直接放入neighbor中
if(visited.count((*it)->label)){
(cnode->neighbors).push_back(visited[(*it)->label]);
}
//如果没有被访问过,则需要进行深度拷贝
else{
UndirectedGraphNode* onode=doCopyGraphNode(*it,visited);
(cnode->neighbors).push_back(onode);
}
}
return cnode;
}
};
说明:这道题是带返回值得递归调用,对图进行深度拷贝。