树和图的存储和dfs,bfs

树是特殊的图,无向图是将有向图两条有向边的有向图

存储有向图有:邻接矩阵 和 邻接表的方法

树的存储

我们采用邻接表的方法

// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;

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

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

树的深度优先遍历

时间复杂度O(n + m), n 表示点数, m 表示边数

DFS:

模板:

int dfs(int u)
{
    st[u] = true; // st[u] 表示点u已经被遍历过

    for (int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j]) dfs(j);
    }
}

AcWing.846树的重心

/*
    树的存储,与树的深度优先遍历
    无向图将有向图边方向相反
*/
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 100010, M = N * 2;// 临接表的一个链可以连接全部结点,有相反两个方向所以翻倍

int h[N], e[M], ne[M], idx = 0;// h[k] 是第k个节点,e[k]是他相连的边,ne[k]是指向它相连边的指针,idx是当前用到哪个空闲空间 
bool st[N];// 这个节点是否走过
int ans = N;  //因为要找最小值,初始为最大

int n;
void add (int a, int b)// 插入到开头
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

// 求以k结点为根的节点的树的结点个数
int dfs (int k)
{
    st[k] = true;
    int sum = 1, size = 0;// sum包含此节点的整个数的结点数量,size是这个节点连接的子树最大节点数
    
    for (int i = h[k]; ~i; i = ne[i])// 遍历与h[k] 相连的所有子树
    {
        int j = e[i];//求得与h[k]相连的所有子树的结点s
        if (st[j]) continue;
        int s = dfs(j);
        sum += s;// sum用来求k节点上面的所有节点数
        size = max (s, size);
    }
    size = max (n - sum, size);
    ans = min (size, ans);
    return sum;
}


int main()
{
    memset(h, -1, sizeof h);
    cin >> n;
    for (int i = 0; i < n; i ++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }
    dfs (1);// 无向图中各个结点是向联通的任意从一个节点都可以走完整个图
    printf("%d", ans);
    return 0;
}

BFS:

模板:

queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
q.push(1);

while (q.size())
{
    int t = q.front();
    q.pop();

    for (int i = h[t]; i != -1; i = ne[i])
    {
        int j = e[i];
        if (!st[j])
        {
            st[j] = true; // 表示点j已经被遍历过
            q.push(j);
        }
    }
}

AcWing.847图中点的层次

/*
    数的存储和宽度优先搜索
    模板:队列,while循环, 队列初始化
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 100010;

int h[N], e[N], ne[N], idx = 0;
int d[N];// 在深度优先遍历中用来st[N] 来存节点是否走过但是在宽度优先遍历中由于要存上一个点的距离所以用d[N]来存
// 可以将d[N]初始化为空-1用于判断是否走过
int n, m;

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

int bfs (int k)
{
    memset(d, -1, sizeof d);
    queue<int> q;
    q.push(k);
    d[k] = 0;// 注意这里初始化填k不然对不上号走不出循环
    
    while (q.size ())
    {
        int t = q.front();
        q.pop();// 用过及时弹出
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            if (d[j] == -1)
            {
                d[j] = d[t] + 1;//由于j表示与t相连的边,i表示t在数组中的位置两者有所区别
                q.push(j);
            }
        }
    }
    
    return d[n];
}

int main ()
{
    memset(h, -1, sizeof h);
    cin >> n >> m;
    for (int i = 0; i < m; i ++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add (a, b);// 有向边只增添一条
    }
    printf("%d", bfs(1));
    return 0;
}

BFS的应用:

拓扑排序:

拓扑序列:拓扑序列是指对有向图来说

  (1)每个顶点出现且只出现一次。

  (2)若存在一条从顶点 A 到顶点 B 的路径,那么在序列中顶点 A 出现在顶点 B 的前面。

            是一个无环图,有向无环图也称为拓扑图

AcWing.848有向图的拓扑序列

/*
    拓扑排序使用的是将入度为零的结点先添加到队列,然后一次访问这些结点的下一个节点,遍历这些结点的时候就弹出
    这样下一个节点就是入度为零的结点了,将它的入度减1,然后入队
    拓扑序列就只需要一次将这些入度为零的结点添加到答案序列即可
*/
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>

using namespace std;

const int N = 100010;

int h[N], e[N], ne[N], idx = 0;// 图的存储模板
//需要用入度判断
int in[N]; //由于是通过入度访问结点所以就不用判读是否访问的结点,因为访问的已经删除了
vector<int> ans;

int n, m;// 节点个数n, 边的个数m

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

void topsort ()
{
    queue<int> q;
    for (int i = 1; i <= n; i ++)// 要遍历每一个结点,而不是一个邻接表
        if (!in[i]) q.push(i); // 将所有入度为零的结点入队
        
    
    while (q.size())
    {
        int t = q.front();
        ans.push_back(t);
        q.pop();
        
        for (int i = h[t]; ~i; i = ne[i])
        {
            int j = e[i];
            in[j] --;// 前一个节点被弹出,入度减1
            if (!in[j]) q.push(j);
        }
    }
}

int main ()
{
    memset(h, -1, sizeof h);
    cin >> n >> m;
    for (int i = 0; i < m; i ++)
    {
        int a, b;
        scanf ("%d%d", &a, &b);
        add (a, b);
        in[b] ++; // 啊,忘记插入的时候入度初始化了
    }
    topsort();
    if (ans.size() < n) puts("-1");
    else {
        for (auto item : ans)
        printf("%d ", item);
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

ˇasushiro

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

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

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

打赏作者

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

抵扣说明:

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

余额充值