一、有向图、无向图?

有向图: a --> b
无向图: a —> b, b —>a
可以把无向图看成特殊的有向图,建两条边就可以了。只需要考虑有向图怎么存储

二、有向图的存储

1.邻接矩阵

开一个二维数组 g[a][b] 表示a – > b,
(不能存储重边)

  • 如果有大小, g[a][b] = 权重
  • 如果没有权重, 可以表示 bool 值,有无道路。
  • 空间复杂度: n * n, 比较适合存储稠密图

2、邻接表

每个点都有一个单链表,表示该点可以走到的点
每个单链表中每个点的顺序无妨

//举例
我们有四个点1,2,3,4, 每个点的单链表存储该点可以通向的点
1---->3 ---> 4 ---->空
2 ----> 1 -----> 4 ---->空
3 ----> 4 ----> 空
4 ---> 空
这样即可存储一个唯一确定的有向图
//如果我们想把 2--->3插进去, 每个单链表的表头为 h[1], h[2]等
我们一般在表头插入
h[2] -- > 3 --> 1--->4--->空
先把 3 指向 1,再把 h[2] 指向 3

h[]存储N个链表的表头, e[]存储每个点的值是多少
ne[]存储的每个节点的next值,即指向的下一个点是多少
两者用下标关联起来,空节点的下标用-1表示

在这里插入图片描述

1.邻接表的初始化

 memset(h, -1, sizeof(h)); //初始化

2. 邻接表的插入

void add(int a, int b){  //插入一条边 a --> b
    e[idx] = b; //先把节点b的值附上
    ne[idx] = h[a];
    h[a] = idx ++;
}

3.举例说明,更清晰的理解邻接表

idx = 1, 全局变量
h[]每个链表的头, e[]存储的每个点的值(非下标)是多少, 
ne[]存储的每个结点的下一个值(下标)
存储
	1--->3 ---> 4--->空
下标:      0-----1  ----  2

	2--->1 ----> 4 ---> 空
下标         3-----4 ------5
我们把这两条输出来,不是插入
h[1] = 1;  //头指向的第一个是下标为1的
e[0] = 1, ne[0] = 1, 
e[1] = 3, ne[1] = 2
e[2] = 4, ne[2] = -1,  //第一条链表结束, idx = 3

h[2] = 3, e[3] = 2, ne[3] = 4,
e[4] = 1, ne[4] = 5
e[5] = 4, ne[5] = -1; 第二条结束: idx  = 6

插入一条 1--->7
e[6] = 7, ne[6] = h[1] = 1, h[1] = 6, idx = 7

三、有向图的遍历

每个点只会遍历一次

深度优先遍历

(一条道走到黑)
在这里插入图片描述

bool st[N]; //记录该点是否遍历过了

void dfs(int u){  //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≤105

分析

  • 题意理解:输出的是去掉重心(树的重心可能不唯一。但是树是唯一的),剩余连通块中 点数 最大值。
  • 对每个点求出删除改点后剩余连通块中点数的最大值,从这些最大值中找出最小的那个 即是答案
  • 无向图:建立边的时候要建立两条相反方向的边
  • 树的dfs,每次更新一下ans,返回以u为根的子树中节点的个数,包括u节点 dfs可以算出子树的大小
  • 在这里插入图片描述
#include <iostream>
#include <cstring>
using namespace std;
const int N = 100010, M = 2 * N;
int ans = N, n, m;
int h[N], e[M], ne[M], idx; 
bool st[N];

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

//返回以 u 为根的子树大小
int dfs(int u){
    st[u] = true;
    int sum = 1; //当前子树的大小,当前这个点算一个点
    int res = 0; //把该点删掉后,连通块大小的最大值
    for(int i = h[u]; i != -1; i = ne[i]){
        int j = e[i];
        if(!st[j]){
            int s = dfs(j);  //当前子树的大小
            res = max(s, res); //当前子树也算一个连通块,和res取一个max 
            sum += s;//该子树是以u为节点的子树的一部分,所以sum要加上s
        }
    }

    res = max(res, n - sum); //去掉每个点后连通块中点数最大值
    ans = min(ans, res); 
    return sum;
}

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xuhx&

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值