树与图的深度优先遍历

树与图的深度优先遍历

树与图的存储

树是一种没有闭环的无向图,无向图是一种特殊的有向图。因此我们只要表示出有向图就可以了。
有向图的表示有两种,分别是邻接图和邻接表。用的比较多的是邻接表。
邻接表的结构就是一个数组拉一个链表和之前谈到的哈希表解决冲突的链表法一样。

例题

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

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

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

输入格式

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

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

输出格式

输出一个整数m,表示重心的所有的子树中最大的子树的结点数目。

数据范围

1n1051≤n≤105

输入样例

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

输出样例:

4

解释题目

树的重心就是去掉该节点以后,树当中剩余连通块中,节点数的最大值最小
而去掉节点以后,一共分为几个部分,上方一个连通块,下方各个子树一个连通块。
考察给出样例的情况:

树与图的深度优先遍历_第1张图片
样例中,假如要去除的是4号节点
树与图的深度优先遍历_第2张图片
剩下就是下方有两颗子树,一棵点数为1 一棵为2 则 上方连通块为 9 - 3 -1
只要是移除节点的情况,都可以分为这样的连通块。

思路

题目分析明白以后,思路应该如何考虑呢?
那我们只要求出每个节点的各个子树的节点数就可以了,在深度优先搜索的时候,把每个子树的节点返回。即可求得该节点各个子树的节点,然后可以求出他们的最小值,还可以求出和。求出和以后,在用 n - sum -1就可以求得上方连通块的点数,然后再求一次最小值即可。
在深度优先搜索之前,我们需要先将图存储下来树与图的深度优先遍历_第3张图片
部分存储情况如上图所示。

然后任意找一个节点,开始深度优先遍历,即可以遍历到所有的节点,然后得到每个节点的子树节点,最后求出答案。

代码实现

链表用数组模拟

//删除某个节点以后,连通块一共分为几个部分:除去当前的节点及其子树为一个部分,这部分的节点数就是总数减去该树
//还有一部分是该节点的各个子节点形成的子数。
//第一步:先要表示出该图,是无向图,每次都需要建立双向的连接
//第二步:用dfs求出每个节点所包含的节点数

#include 
#include 

using namespace std;

const int N = 100010;

//表示图需要一个数组加一组链表,用数组模拟
int h[N], e[2 * N], ne[2 * N], idx;
bool st[N];
int ans = N;
int n;

//模拟链表,从头结点插入b,h[a]中存放链表头结点的idx,ne[idx]中就是存放e[idx]指向的下一个节点idx
//步骤:先存下b,然后把h[a]放入ne存为下一个节点(改变next指针),然后把h[a]指向idx(头结点指向新元素),idx++;
void insert(int a, int b)
{
     
    e[idx] = b;
    ne[idx] = h[a];//当前的h[a]成为当前的e[idx]的下一个节点
    h[a] = idx++;
}

//返回的是以u为根的子树的大小
int dfs(int u)
{
     
    st[u] = true;//标记一下已经遍历过了
    int sum = 1, res = 0; //sum表示当前子树的大小,res表示删除该节点后每个子连通块的大小
    for(int i = h[u]; i != -1; i = ne[i])
    {
     
        int j = e[i];//搜链表
        if(!st[j])
        {
     
            int s = dfs(j);//返回j子树的节点数大小
            
            res = max(res, s);//取最大值
            sum += s;//该条链表上的所有子树的节点数加起来
        }
    }
    
    res = max(res, n - sum);//上方的树减去该节点的树
    ans = min(ans, res);
    
    return sum;
     
}
int main()
{
     
    
    cin >> n;
    memset(h, -1, sizeof h);
    
    int a, b;
    for(int i = 0; i < n; i++) //因为是无向图,所以每次都要插入双向的
    {
     
        scanf("%d%d", &a, &b);
        insert(a,b);
        insert(b,a);
    }
    dfs(1);//选1开始遍历
    
    cout << ans << endl;
    return 0;
}

以搜1号节点为例,第一层函数栈中,j是2,7,4,s分别是3,1,4然后res是4,sum是3+1+4+1是9,因此上方连通块是0;
树与图的深度优先遍历_第4张图片

你可能感兴趣的:(算法基础课,树与图的深度优先遍历)