搜索与图论---树和图的遍历

1.树和图的存储

树是一种特殊的图。树是无环、连通图。

1.1有向图

在这里插入图片描述

1.1.1 邻接矩阵
g[a,b]	存储a->b

空间复杂度n^2

1.1.2邻接表

为每一个点开了一个单链表,存储可以到达的位置。
在这里插入图片描述
在这里插入图片描述
代码实现


#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010, M = N * 2;

int n;
int h[N]; //h存放n个链表的链表头
int e[M];//e存放每一个结点的值
int ne[M];//ne存放每个结点的next值
int idx;

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

int main()
{
    //将所有头结点置为-1
    memset(h,-1,sizeof h);
}
1.2无向图

在这里插入图片描述

2.树和图的遍历

2.1深度优先遍历(一条道走到黑)

在这里插入图片描述

void dfs(int u)//u表示当前dfs已经到哪一个点了
{
    st[u] = true;//标记一下,已经被搜过了
    
    //遍历一下u的所有初边
    for(int i = h[u];i != -1;i = ne[i])
    {
        //j存放当前链表里面的结点对应图里面点的编号是多少
        int j = e[i];
        if(!st[j])  dfs(j);//没有搜索到,继续搜
    }
}

例题:树的重心 (难)
在这里插入图片描述
题意:
在这里插入图片描述
最优解为4.
在这里插入图片描述
代码


#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010, M = N * 2;

int n,m;
int h[N]; //h存放n个链表的链表头
int e[M];//e存放每一个结点的值
int ne[M];//ne存放每个结点的next值
int idx;
bool st[N];//存储哪些点已经被遍历过

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

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

// dfs 框架
/*
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);
        }
    }
}
*/

//返回以u为跟的字树中点的数量,包括u节点
int dfs(int u)//u表示当前dfs已经到哪一个点了
{
    st[u] = true;//标记一下访问过u结点
    
    int sum = 1;//存储 删掉某个节点之后,最大的连通子图节点数
    int res = 0;//存储 以u为根的树 的节点数, 包括u,如图中的4号节点
    //遍历一下u的所有初边
    for(int i = h[u];i != -1;i = ne[i])
    {
        //j存放当前链表里面的结点对应图里面点的编号是多少
        int j = e[i];
        //因为每个节点的编号都是不一样的,所以 用编号为下标 来标记是否被访问过
        if(!st[j]) 
        {
            int s = dfs(j); // u节点的单棵子树节点数 如图中的size值
            res = max(res,s);// 记录最大联通子图的节点数
            sum += s;//以j为根的树 的节点数
        }
    }
      //n-sum 如图中的n-size值,不包括根节点4;
    res = max(res,n - sum);// 选择u节点为重心,最大的 连通子图节点数
    //遍历过的假设重心中,最小的最大联通子图的 节点数
    ans = min(ans,res);
    
    return sum;
}




int main()
{
    
    scanf("%d", &n);//表示树的结点数

    //将所有头结点置为-1
    memset(h,-1,sizeof h);

     // 题目接下来会输入,n-1行数据,
    // 树中是不存在环的,对于有n个节点的树,必定是n-1条边
    for (int i = 0; i < n - 1; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b), add(b, a);
    }

    dfs(1);//可以任意选定一个节点开始 u<=n

    printf("%d\n", ans);

    return 0;
    
    
    dfs(1);
}
2.2宽度优先遍历(按层搜)

在这里插入图片描述
题目:图中点的层次
在这里插入图片描述
在这里插入图片描述
思路
在这里插入图片描述
代码

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;

const int N = 100010;

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 ++ ;
}

int bfs()
{
    memset(d, -1, sizeof d);

    queue<int> q;
    d[1] = 0;
    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 (d[j] == -1)
            {
                d[j] = d[t] + 1;
                q.push(j);
            }
        }
    }

    return d[n];
}

int main()
{
    scanf("%d%d", &n, &m);
    memset(h, -1, sizeof h);

    for (int i = 0; i < m; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);
    }

    cout << bfs() << endl;

    return 0;
}


3.图的拓补序列(针对有向图)

在这里插入图片描述

所有边都是从前指向后的。

  • 入度
  • 出度

一个有向无环图,至少存在一个入度为0的点。
在这里插入图片描述
代码

#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int N = 100010;

int n, m;
int h[N], e[N], ne[N], idx;//邻接表
int d[N];//入度
int q[N];//队列

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

bool topsort()
{
    //队头,队尾
    int hh = 0, tt = -1;

    //将所有入度为0的点插入队列中
    for (int i = 1; i <= n; i ++ )
        if (!d[i])
            q[ ++ tt] = i;

    //队列不空
    while (hh <= tt)
    {
        int t = q[hh ++ ];

        for (int i = h[t]; i != -1; i = ne[i])
        {
            int j = e[i];
            if (-- d[j] == 0)//入度--
                q[ ++ tt] = j;
        }
    }

    //判断是否所有点都入队
    return tt == n - 1;
}

int main()
{
    scanf("%d%d", &n, &m);

    memset(h, -1, sizeof h);

    for (int i = 0; i < m; i ++ )
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b);

        d[b] ++ ;//入度数
    }

    if (!topsort()) puts("-1");
    else
    {
        for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);
        puts("");
    }

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Next---YOLO

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

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

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

打赏作者

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

抵扣说明:

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

余额充值