算法学习笔记(23)——树与图的DFS与BFS

树与图的DFS与BFS

首先,树是一种特殊结构的图,所以树与图的存储是相同的,而图又分为有向图与无向图,对无向图我们可以在两个点之间添加两条边。

有向图的存储方式主要有两种

  • 稀疏图(点多边少)一般用邻接表存储
  • 稠密图(点少边多)一般用邻接矩阵存储。

在邻接表的实现中,用数组h来记录每个节点向外的边的链表头指针,初始时都是空(即-1)。用idx来表示链表节点的分配位置。数组e表示节点的值,即是目标节点的编号。数组ne表示节点的next指针,也就是链表节点的下一节点被分配的下标。

注意,当用邻接表去存无向图(或者树)的时候,因为a → bb → a的边都要存,所以放置链表节点信息的e数组和ne数组至少要开到边总数目的两倍大。

DFS

题目链接:AcWing 846. 树的重心

#include <iostream>
#include <cstring>

using namespace std;

const int N = 1e5 + 10, M = N * 2;

int n;
int h[N], e[M], ne[M], idx; // 邻接表存储树,无向边所以e与ne数组开两倍大小
bool st[N];  // 标记节点是否被访问过
int ans = N; // 记录答案(各个连通块中点数的最大值)

// 添加一条a到b的有向边
void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

int dfs(int u)
{
    // 标记当前节点已经被访问
    st[u] = true;
    
    // sum存储以当前节点为根的子树大小
    // res存储当前节点的最大子连通块
    int sum = 1, res = 0;
    
    // 遍历与当前节点相邻的所有节点
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        // 如果j节点没有被访问过
        if (!st[j]) {
            int t = dfs(j);     // t为以j为根的子树大小
            res = max(res, t);  // 更新最大子连通块的值
            sum += t;           // 子树大小加上这一子连通块
        }
    }
    res = max(res, n - sum);    // 利用以u号节点为根的子树之外的部分更新最大连通块
    ans = min(ans, res);        // 更新以不同节点为重心的最大连通块的值
    
    return sum;                 // 返回以当前节点为根的子树大小
}

int main()
{
    memset(h, -1, sizeof h);    // 初始化邻接表
    
    cin >> n;
    
    // 注意,此处不能使用while(n --),这会改变n的值
    for (int i = 0; i < n - 1; i ++ ) {
        int a, b;
        cin >> a >> b;
        add(a, b), add(b, a);
    }
    
    dfs(1); // 从1号节点开始搜索
    
    cout << ans << endl;
    
    return 0;
}

BFS

#include <iostream>
#include <queue>
#include <cstring>

using namespace std;

const int N = 1e5 + 10;

int n, m;
int h[N], e[N], ne[N], idx;
int d[N];

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

// 宽搜套路,返回到1~n的最短距离
int bfs()
{
    queue<int> q;
    q.push(1);
    // 一般情况下距离数组初始化为正无穷,但本题中若不能到达则返回-1,所以用-1代表不可达
    memset(d, -1, sizeof d);
    d[1] = 0;
    
    while (q.size()) {
        int t = q.front();
        q.pop();
        
        for (int i = h[t]; i != -1; i = ne[i]) {
            int j = e[i];
            // 如果当前点还没有访问过
            if (d[j] == -1) {
                d[j] = d[t] + 1;
                q.push(j);
            }
        }
    }
    
    return d[n];
}

int main()
{
    memset(h, -1, sizeof h);
    
    cin >> n >> m;
    while (m -- ) {
        int a, b;
        cin >> a >> b;
        add(a, b);
    }
    
    cout << bfs() << endl;
    
    return 0;
}
posted @ 2022-12-09 22:07  S!no  阅读(83)  评论(0编辑  收藏  举报