BFS和DFS主要用于连通图遍历,应用广泛,且模版性强,可举一反三
BFS主要应用于连通图的遍历,它的核心思想是从一个顶点开始,辐射状地优先遍历其周围较广的区域,即逐层遍历,BFS最经典的应用场景为最短路径,很多最短路径算法都是基于BFS实现,BFS通常基于队列的思想实现,其实现过程如下:
(1)顶点入队列
(2)队列为空,算法结束,队列非空,算法继续执行
(3)出队列取得队头元素
(4)查找队头元素所有子节点,未遍历则依次入队
(5)转步骤2
/* BFS 伪代码 */
bool BFS(Node n){
Queue q; //定义队列
q.push(n); //顶点节点入栈
visit(n);
while(!q.empty()) {
Node front = q.front();
if (check(front)) {
return true; //检查节点状态是否为目标状态
}
q.pop(); //队首出队
for (Node x in front) { //遍历当前节点的邻节点
if (!visited(x)) {
q.push(x); //为访问的节点进入队列
visit(x); //标记当前节点已访问
}
}
}
return false;
}
深度优先搜索DFS(Depth First Search)主要应用于连通图的遍历,是从初始结点开始扩展,扩展顺序总是先扩展最新产生的结点。这就使得搜索沿着状态空间某条单一的路径进行下去,直到最后的结点不能产生新结点或者找到目标结点为止。当搜索到不能产生新的结点的时候,就沿着结点产生顺序的反方向寻找可以产生新结点的结点,并扩展它,形成另一条搜索路径。
为了便于进行搜索,要设置一个表存储所有的结点。由于在深度优先搜索算法中,要满足先生成的结点后扩展的原则,所以存储结点的表一般采用栈这种数据结构。
深度优先搜索算法的搜索步骤一般是:
(1)从初始结点开始,将待扩展结点依次放到栈中。
(2)如果栈空,即所有待扩展结点已全部扩展完毕,则问题无解,退出。
(3)取栈中最新加入的结点,即栈顶结点出栈,并用相应的扩展原则扩展出所有的子结点,并按顺序将这些结点放入栈中。若没有子结点产生,则转(2)。
(4)如果某个子结点为目标结点,则找到问题的解(这不一定是最优解),结束。如果要求得问题的最优解,或者所有解,则转(2),继续搜索新的目标结点。
/* DFS 伪代码 */
//递归版
bool DFS(Node n){
if (check(n)) {
return true; //检查节点状态是否为目标状态
}
for (Node x in n) { // 遍历n相邻节点
if (!visited(x)) { //跳过已遍历的节点
visit(x); //标记当前节点已访问
if (DFS(x)) { //判断是否搜索出解
return true;
}
unvisit(x); //若要遍历所有路径,需要重置节点状态,若只需遍历所有节点,则不需要重置节点状态
}
}
return false; //本次搜索无解
}
//非递归版
bool DFS(Node n){
Stack s; //
s.push(n);
while (!s.empty()) {
if (visited(s.top())) {
unvisit(s.top()); //若要遍历所有路径,需要重置节点状态,若只需遍历所有节点,则不需要重置节点状态
s.pop(); //跳过已遍历的节点
continue;
}
if (check(s.top())) {
return true; //检查节点状态是否为目标状态
}
visit(s.top()); //标记当前节点已访问
for (Node x in n) { // 遍历n相邻节点
if (!visited(x))
s.push(x); //将访问的节点进栈
}
}
return false; //搜索无解
}
//上下左右方向遍历
const int dir[5] = {0, 1, 0, -1, 0};
for (int i = 0; i < 4; i++) {
x = x + dir[i];
y = y + dir[i + 1];
}