树和图的深度优先搜索(应用:树的重心)

树和图的存储

树是一种没有闭环的无向图,无向图是一种特殊的有向图。因此我们只要表示出有向图就可以了。有向图的表示有两种,分别是邻接矩阵和邻接表。用的比较多的是邻接表。邻接表的结构就是一个数组拉一个链表

数组建立邻接表

//邻接表
int h[N], e[N * 2], ne[N * 2], idx;

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

树的bfs模板

邻接表:
1 -> 2
2 -> 1 -> 3
3 -> 2 -> 4 - > 6
4 ->3 -> 5
5 -> 4
6 -> 3 -> 7
7 -> 6

以第u个节点为起点进行深搜:(例如节点1)
先搜索与他相连的第一个节点一路到底,搜索完了再回溯搜与之相连的第二个节点,本图中与1向量的只有2,因此搜索以2为节点的。

在这里插入图片描述

// 需要标记数组st[N]
void dfs(int u) {
    st[u] = true;  
    for (int i = h[u]; i != -1; i = ne[i]) {
        int j = e[i];
        if (!st[j]) {
            dfs(j);
        }
    }
}

例题:树的重心

题目描述

给定一颗树,树中包含n个结点(编号1~n)和n-1条无向边。

请你找到树的重心,并输出将重心删除后,剩余各个连通块中点数的最大值。

重心定义:重心是指树中的一个结点,如果将这个点删除后,剩余各个连通块中点数的最大值最小,那么这个节点被称为树的重心。

输入格式

第一行包含整数n,表示树的结点数。

接下来n-1行,每行包含两个整数a和b,表示点a和点b之间存在一条边。
输出格式

输出一个整数m,表示将重心删除后,剩余各个连通块中点数的最大值。
数据范围

1≤n≤10^5

输入样例

9
1 2
1 7
1 4
2 8
2 5
4 3
3 9
4 6

输出样例:

4

思路:可以用树的深度优先搜索,对于每个节点求出去掉他后剩余各个连通块中点数的最大值,例如去掉4后下面的两个连通块分别是以3和6为根的子树中节点的数量,然后顶上那个是 n - size3 - size 6 - 1

int dfs(int u) ; 求出以u为根的树中节点的数量,所以在求第去掉第i个节点后所有连通块的最大值就可以用max(dfs(与u直接相连的节点), n - dfs(与u直接相连的节点) - 1 )

在这里插入图片描述
代码

#include <iostream>
#include <cstring>
using namespace std;

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

int idx,h[N],e[M],ne[M];
bool st[N];   //存储哪些点被遍历过了
int n;

int ans = N;    //全局答案,最小的最大值


void add(int a,int b);

int dfs(int u);

int main(){
    
    cin>>n;
    memset(h, -1, sizeof h);
    for(int i = 0;i<n-1;i++){
        int a,b;
        cin>>a>>b;
        add(a,b);
        add(b,a);
    }
    
    dfs(1);
    cout<<ans<<endl;
    
    return 0;
}

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

}


//树和图的深度优先搜索
//返回以u为根的子树中节点的数量
int dfs(int u){
    
    int sum = 1;   //当前树的大小
    int res = 0;   //当前点删除后每个子树的最大值
    st[u] = true;   //标记一下,已经被搜过了
    for(int i = h[u];i != -1 ; i = ne[i]){
        int j = e[i];
        if(!st[j]){
            int t = dfs(j);
            res = max(res,t);
            sum += t;
        }
            
           
    }
    
    res = max(n - sum,res);
    ans = min(ans,res);
    return sum;
   
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值