首页 > 其他分享 >搜索与图论(一)树的遍历/深度/广度/拓扑排序

搜索与图论(一)树的遍历/深度/广度/拓扑排序

时间:2024-03-17 16:58:41浏览次数:22  
标签:图论 遍历 idx int 拓扑 入度 队列 节点

文章目录

搜索与图论

树与图的深度优先遍历

举个栗子

在这里插入图片描述

树的重心

在这里插入图片描述

思路

邻接表存储

for(int i=1;i<=n;i++)  
{
    cout<<i<<":";
    for(int j=h[i];j!=-1;j=ne[j])
    {
        cout<<"->"<<e[j]; 
    }
    cout<<endl;
}
return 0;

输出树结构:

1:->4->7->2
2:->5->8->1
3:->9->4
4:->6->3->1
5:->2
6:->4
7:->1
8:->2
9:->3


结论

在本题的邻接表存储结构中,有两个容易混淆的地方,一个是节点的编号,一个是节点的下标。
节点的编号是指上图所画的树中节点的值,范围是从1~n。在本题中,每次输入的a和b就是节点的编号,编号用e[i]数组存储。
节点的下标指节点在数组中的位置索引,数组之间的关系就是通过下标来建立连接,下标用idx来记录。idx范围从0开始,如果idx==-1表示空。

e[i]的值是编号,是下标为i节点的编号。
ne[i]的值是下标,是下标为i的节点的next节点的下标。
h[i]存储的是下标,是编号为i的节点的next节点的下标,比如编号为1的节点的下一个节点是4,那么我输出e[h[1]]就是4

代码如下
#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;

const int N = 1e5 + 10; //数据范围是10的5次方
const int M = 2 * N; //以有向图的格式存储无向图,所以每个节点至多对应2n-2条边

int h[N]; //邻接表存储树,有n个节点,所以需要n个队列头节点
int e[M]; //存储元素
int ne[M]; //存储列表的next值
int idx; //单链表指针
int n; //题目所给的输入,n个节点
int ans = N; //表示重心的所有的子树中,最大的子树的结点数目

bool st[N]; //记录节点是否被访问过,访问过则标记为true

//a所对应的单链表中插入b  a作为根 
void add(int a, int b) {
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

//返回以u为根的子树中节点的个数,包括u节点
int dfs(int u) {
    int res = 0; //存储 删掉某个节点之后,最大的连通子图节点数
    st[u] = true; //标记访问过u节点
    int sum = 1; //存储 以u为根的树 的节点数, 包括u,如图中的4号节点

    //访问u的每个子节点
    for (int i = h[u]; i != -1; i = ne[i]) {
        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(res, ans); //遍历过的假设重心中,最小的最大联通子图的 节点数
    return sum;
}

int main() {
    memset(h, -1, sizeof h); //初始化h数组 -1表示尾节点
    cin >> n; //表示树的结点数

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

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

    cout << ans << endl;

    return 0;
}

树与图的广度优先遍历

举个例子
图中点的层次

在这里插入图片描述

样例展示
代码
#include <iostream>
#include <queue>
#include <queue>
#include <cstring>
using namespace std;
const int N = 100010;
int idx, e[N], h[N], ne[N], d[N];
int n, m;
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;
    q.push(1);
    d[1] = 0;
    
    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() {
    cin >> n >> m;
    memset(h, -1, sizeof h);    //初始化必须有h[] = -1
    int a, b;
    for(int i = 0; i < m; i++) {
        scanf("%d%d", &a, &b);
        add(a, b);
    }
    cout << bfs() << endl;
    return 0;
}

拓扑排序

啥是拓扑排序?
  • 一个有向图,如果图中有入度为 0 的点,就把这个点删掉,同时也删掉这个点所连的边。

  • 一直进行上面出处理,如果所有点都能被删掉,则这个图可以进行拓扑排序。

在这里插入图片描述

这时整个图被删除干净,所有能进行拓扑排序。


解题思路

首先记录各个点的入度

然后将入度为 0 的点放入队列

将队列里的点依次出队列,然后找出所有出队列这个点发出的边,删除边,同事边的另一侧的点的入度 -1。

如果所有点都进过队列,则可以拓扑排序,输出所有顶点。否则输出-1,代表不可以进行拓扑排序。


举个栗子

有向图中的拓扑排序

题目
代码如下
**
首先记录各个点的入度
然后将入度为 0 的点放入队列

将队列里的点依次出队列,然后找出所有出队列这个点发出的边,删除边,同事边的另一侧的点的入度 -1。

如果所有点都进过队列,则可以拓扑排序,输出所有顶点。否则输出-1,代表不可以进行拓扑排序。
**
#include<iostream>
#include<cstring>
using namespace std;
const int N = 100010;
int e[N],ne[N],idx; //邻接表存储图
int h[N];       //头结点
int q[N],hh = 0,tt = -1;    //队列保存入度为0的点
int n,m;    //存储图的点数和边数
int d[N];   //保存各个点的入度

void add(int a,int b)   //将b加在a的后边
{
    e[idx] = b;
    ne[idx] = h[a];
    h[a] = idx++;
}

void topsort()
{
    //遍历一遍顶点的入度
    for(int i = 1;i <= n;i++)
    {
        if(d[i] == 0) 
        q[++tt] = i; //如果入度为0,可以进队列
    }
    while(hh <= tt) //循环处理队列中点
    {
        int a = q[hh++];
        //循环删除a发出的边
        for(int i = h[a];i != -1;i = ne[i])
        {
            int b = e[i];
            //相关点入度-1
            d[b]--;
            if(d[b] == 0)
            //若b的入度减为0,则可以输出,进队列
            q[++tt] = b;
        }
       
    }
    //如果队列中点的个数等于图中点的个数,则可以进行拓扑排序
    //输出队列
    if(tt == n - 1)
    {
        for(int i = 0;i < n;i++)
        {
            printf("%d ",q[i]);
        }
    }
    else cout << -1;    //输出-1,代表错误
}

int main()
{
    cin >> n >> m;  //保存点的个数和边的个数
    memset(h,-1,sizeof h);  //初始化邻接矩阵
    while(m--)
    {   //依次读入边
        int a,b;
        cin >> a >> b;
        add(a,b);   //添加到邻接矩阵
        d[b]++;     //顶点b的入读+1
        
    }
    topsort();//进行拓扑排序
    return 0;
}

标签:图论,遍历,idx,int,拓扑,入度,队列,节点
From: https://blog.csdn.net/m0_72256122/article/details/136612373

相关文章

  • 数据结构笔记(十四)二叉树的遍历(递归)
    四种访问方式:前序遍历,中序遍历,后序遍历,层序遍历这篇文章主要为前序,中序,后序遍历的递归形式,递归形式较为简单,后面更新遍历的循环形式较为复杂,建议使用递归形式#include<stdio.h>#include<stdlib.h>typedefcharE;typedefstructTreeNode*Node;structTreeNod......
  • 【洛谷 P8602】[蓝桥杯 2013 省 A] 大臣的旅费 题解(图论+深度优先搜索+树的直径+链式
    [蓝桥杯2013省A]大臣的旅费题目描述很久以前,T王国空前繁荣。为了更好地管理国家,王国修建了大量的快速路,用于连接首都和王国内的各大城市。为节省经费,T国的大臣们经过思考,制定了一套优秀的修建方案,使得任何一个大城市都能从首都直接或者通过其他大城市间接到达。同......
  • chapter11-图论
    1.图的存储方式首先,关于图的存储方式有2种,一种是邻接矩阵,一种是邻接表,而邻接表适用于1个点对到其他所有点的批处理,实际程序中经常使用。邻接表会给每一个顶点建立一个单链表,即使那个顶点没有度(无向图),or没有任何出度(有向图)。在程序中,我们并不是使用单链表来存储,而是一个向量数组......
  • 【C++算法模板】图论-拓扑排序,超详细注释带例题
    文章目录0)概述1)Kahn算法1:数据结构2:建图3:Kanh算法2)DFS染色1:数据结构2:建图3:DFS3)算法对比【例题】洛谷B3644推荐视频链接:D01拓扑排序0)概述给定一张有向无环图,排出所有顶点的一个序列A......
  • 二叉树的迭代遍历
    二叉树前后序遍历(迭代)#include<bits/stdc++.h>usingnamespacestd;structNode{intdata;Node*left;Node*right;Node(intvalue=0):data(value),left(nullptr),right(nullptr){}};Node*insertEle();voidpreorder(Node*pNode);voidmid......
  • 图论——倍增LCA 学习笔记
    图论——倍增LCA学习笔记定义最近公共祖先,简称LCA(LowestCommonAncestor)。一个集合\(S\)的最近公共祖先\(\text{LCA}(S)=\text{LCA}(s_1,s_2,\dots,s_k)\)定义为:这个集合中所有节点,其祖先的交集中,离根最远的那个。性质在数值的关系上:\(\text{LCA}(\{u\})=u\);\(\t......
  • 【leetcode】二叉树的前序遍历➕中序遍历➕后序遍历
    大家好,我是苏貝,本篇博客带大家刷题,如果你觉得我写的还不错的话,可以给我一个赞......
  • 全量知识系统 因子分析+在线处理+实时库+生存拓扑控制+跨语言 的设想及百度AI答问 之3
    Q12.进一步,在因子分析+在线处理+生存拓扑控制的基础上,三种实时表的在线处理程序由三个不同程序语言的代理类来代理,以分离不同目标机的编程语言环境的影响。(因为,这里要限制目标编程语言中的数据类型以简化实现过程,并方便适应不同的应用需求).这三个代理类分别是:PythonBroker......
  • 图论:DFS与BFS
    目录1.DFS(图论)1.1.DFS过程1.2.应用2.BFS(图论)2.1.BFS过程2.2.应用2.3.双端队列BFS实现2.4.优先队列BFS(堆优化Dijkstra算法)1.DFS(图论)DFS全称是,中文名是深度优先搜索,是一种用于遍历或搜索树或图的算法。所谓深度优先,就是说每次都尝试向更深的节点走。广义上的DFS:DF......
  • 二叉树的垂序遍历
    说在前面......