算法笔记 / A LEAF IN THE GARDEN

图遍历

算法笔记 · 算法理论 / 图论。来自 Daily Learning 的学习笔记。

深度优先遍历

步骤

  • 沿一条路径访问,直到没有出边
  • 回溯到上一个节点,沿另一条路径访问。
  • 直到所有节点都被访问。

实现

以链式前向星为例

vector<bool> vis(n, false);
void dfs(int u){
    // 标记为已访问
    vis[u] = true;
    // 访问节点u(按具体需求)
    cout << u << ' ';
    // 遍历u的出边
    for(int i = head[u];i;i = G[i].next){
        // 访问出边终点
        int v = G[i].to;
        if(!vis[v]) dfs(v);
    }
}
// 开始遍历
dfs(1);

用途

统计连通块数量

int ans = 0;
// 访问所有节点
for(int i = 1;i <= n;i++){
    // 没访问过,说明是新的连通块
    if(!vis[i]) dfs(i), ans++;
}

广度优先遍历

步骤

  • 访问与源距离为1的节点
  • 访问与源距离为2的节点
  • 依次类推,直到所有节点都被访问。

实现

以链式前向星为例

vector<bool> vis(n, false);
void bfs(int u){
    // 候选队列
    queue<int> q;
    // 先将源点入队
    q.push(u);
    vis[u] = true;
    // 广度优先遍历
    while(!q.empty()){
        // 取出队首节点
        int cur = q.front();
        q.pop();
        // 访问节点cur(按具体需求)
        cout << cur <<'';
        // 遍历cur的出边
        for(int i = head[cur];i;i = G[i].next){
            // 访问出边终点
            int v = G[i].to;
            if(!vis[v]){
                // 入队并标记
                q.push(v);
                vis[v] = true;
            }
        }
    }
}

用途

无权最短路径

// 记录所有点到源点的距离
vector<int> dis(n, -1);
int shortcut(int start, int end, int n){
    // 初始化
    dis.assign(n, -1);
    dis[start] = 0;
    // 广度优先遍历
    queue<int> q;
    q.push(start);
    while(!q.empty()){
        int cur = q.front();
        q.pop();
        if(cur == end) break;
        for(int i = head[cur];i;i = G[i].next){
            int v = G[i].to;
            // 下一距离 = 当前距离 + 1(无权图默认权)
            if(dis[v] == -1){
                dist[v] = dist[cur] + 1;
                q.push(v);
            }
        }
    }
    return dis[end];
}

附:链式前向星

// 每个节点的第一条出边
vector<int> head;
struct Edge{
    // 下一条边的编号
    int next = 0;
    // 边的终点
    int to = 0;
}
// 图的边
vector<Edge> G;
int cnt = 0;