BFS和DFS的一般讲解以及一般模版

PTA 图(上)列出连通集

基本上是模版题,就是用BFS和DFS去遍历图。

BFS和DFS都是图和搜索问题中很基础的算法,也是基本上所有算法的基础,包括A*,IDA*,迭代加深,双向广搜等。所以对于基础一定要多加重视。

DFS主要通过递归实现。一般模版如下。这里用的是费用矩阵。

void dfs(int v) {
    vis[v] = 1;
    for (int i = 0; i < V; i++) {
        if (vis[i] || dst[v][i] != 1) continue;
        dfs(i);
    }
}

基础dfs很简单,唯一要注意的就是是否需要回溯,如果需要回溯,那么一定要有撤销标记的操作,代码如下,不理解的话可以多做几个回溯的题,
这里推荐一下比较经典的八皇后问题。

void dfs(int v) {
    vis[v] = 1;
    for (int i = 0; i < V; i++) {
        if (vis[i] || dst[v][i] != 1) continue;
        dfs(i);
    }
    vis[v] = 0; // <----回溯
}

也可以用dfs去生成全排列

还有值得说的就是,dfs可以去进行记忆化搜索,也就是大家熟悉的动态规划的递归版本,一般比起状态转移方程,从搜索的角度去理解动态规划,会很好接收。
大概长这个样子。

int dp[n][n];
int rec(int i, int j) {
	// recursive basecase
	if (dp[i][j] != -1) return dp[i][j]; // if you have done 	the job before, don't do it again.
	// the search operation you need
	
	return dp[i][j] = res;  // create the memo.
}

接下来是bfs, 这也是搜索中一个很神的算法,适合求最短路径)。
为什么bfs一旦到达目标节点就是最短距离呢,可以这样考虑,bfs总是优先去搜索距离当前节点最近的节点,然后逐步向外扩展,所以一旦到达目标状态,就是距离其实状态最近的。

搜索问题中需要注意状态节点的设计,以及考虑是否需要自定义队列而不是使用库,这取决的题目的要求,若要求输出从起点到终点的路径,那么可能会需要自己写队列,并在节点中保存父节点在队列中的下标,就可以一步一步回退到起点位置。

同时注意bfs什么时候适合用优先队列,在一般的搜索问题中用的是普通队列,是因为在无权图中,(把每个状态看作图中的一个节点),每个节点到它周围相邻节点的距离都是1,没有什么成本费用的差别,换句话说每个节点的优先级别都是相同的,自然也就不要用到优先队列。但是如果对于有向图来说,就没有这么简单了,个人觉得一个很直白的例子就是堆优化的dijkstra算法,这个之后想起来再说把。

那么在图里不需要考虑这些,节点就是int,代表每一个点的标号。
一般的模版如下。

void bfs(int v) {
    que.push(v);
    vis[v] = 1; // <------正确位置
    while(!que.empty()) {
        int p = que.front(); que.pop();
        for (int i = 0; i < V; i++) {
            if (vis[i] || dst[p][i] != 1) continue;
            que.push(i);
            vis[i] = 1; // <------正确位置
        }
    }
}

注意设置vis操作的位置不能错,可以思考下为什么下面的代码是错误的。

void bfs(int v) {
    que.push(v);
    while(!que.empty()) {
        int p = que.front(); que.pop();
        vis[p] = 1;  // <-----错误位置
        for (int i = 0; i < V; i++) {
            if (vis[i] || dst[p][i] != 1) continue;
            que.push(i);
        }
    }
}

接下来就是这道题本身了,题目要求对于每个顶点按照编号从小到大的顺序访问邻节点,那么最好是用矩阵,不要用邻接表(顺序无关紧要)

下面是这个题的代码。

#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int max_n = 20;
int dst[max_n][max_n];
vector<int> Vt(max_n); //  存放连通集的顶点
queue<int> que;
int V, E;
bool vis[max_n];
void dfs(int v) {
    vis[v] = 1;
    Vt.push_back(v);
    for (int i = 0; i < V; i++) {
        if (vis[i] || dst[v][i] != 1) continue;
        dfs(i);
    }
}

void solve_dfs() {
    memset(vis, 0, sizeof(vis));
    for (int i = 0; i < V; i++) {
        if (vis[i]) continue;
        Vt.clear();
        dfs(i);
        printf("{ ");
        for (auto v : Vt) printf("%d ", v);
        printf("}\n");
    }
}

void bfs(int v) {
    que.push(v);
    vis[v] = 1;
    while(!que.empty()) {
        int p = que.front(); que.pop();
        Vt.push_back(p);
        for (int i = 0; i < V; i++) {
            if (vis[i] || dst[p][i] != 1) continue;
            que.push(i);
            vis[i] = 1;
        }
    }
}

void solve_bfs() {
    memset(vis, 0, sizeof(vis));
    for (int i = 0; i < V; i++) {
        if (vis[i]) continue;
        Vt.clear();
        bfs(i);
        printf("{ ");
        for (auto v : Vt) printf("%d ", v);
        printf("}\n");
    }
}

int main() {
    scanf("%d%d", &V, &E);
    for (int i = 0; i < E; i++) {
        int a, b;
        scanf("%d%d", &a, &b);
        dst[a][b] = dst[b][a] = 1; // 邻接矩阵
    }

    solve_dfs();
    solve_bfs();
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值