目录

  • 一、树与图的深度优先遍历及树的一些性质
    • 1.树与图的深度优先遍历
    • 2.时间戳
    • 3.树的DFS序(树链剖分前驱知识)
    • 4.树的深度
    • 5.树的重心与sizesizesize
    • 6.图的连通块划分
  • 二、树与图的广度优先搜索
  • 三、拓扑排序
    • AcWing 164. 可达性统计

声明:
本系列博客是《算法竞赛进阶指南》+《算法竞赛入门经典》+《挑战程序设计竞赛》的学习笔记,主要是因为我三本都买了 按照《算法竞赛进阶指南》的目录顺序学习,包含书中的少部分重要知识点、例题解题报告及我个人的学习心得和对该算法的补充拓展,仅用于学习交流和复习,无任何商业用途。博客中部分内容来源于书本和网络(我尽量减少书中引用),由我个人整理总结(习题和代码可全都是我自己敲哒)部分内容由我个人编写而成,如果想要有更好的学习体验或者希望学习到更全面的知识,请于京东搜索购买正版图书:《算法竞赛进阶指南》——作者李煜东,强烈安利,好书不火系列,谢谢配合。


下方链接为学习笔记目录链接(中转站)

学习笔记目录链接


ACM-ICPC在线模板


注:以下图的建立都是使用链式前向星建图。

int head[N],ver[N],nex[N],edge[N],tot;void add(int u,int v,int val){//链式前向星建图ver[++tot] = v;edge[tot] = val;nex[tot] = head[u];head[u] = tot;
}

一、树与图的深度优先遍历及树的一些性质

1.树与图的深度优先遍历

深度优先遍历,就是在每个点x上面的的多条分支时,任意选择一条边走下去,执行递归,直到回溯到点x后再走其他的边

int vis[N];//标记每一个点的状态void dfs(int u){vis[u] = 1;for(int i = head[u];i;i = nex[i]){int v = ver[i];if(vis[v])continue;dfs(v);}
}

2.时间戳

按照上述的深度优先遍历的过程,以每一个结点第一次被访问的顺序,依次赋值1~N的整数标记,该标记就被称为时间戳。
标记了每一个结点的访问顺序。

3.树的DFS序(树链剖分前驱知识)

一般来说,我们在对树的进行深度优先时,对于每个节点,在刚进入递归时和回溯前各记录一次该点的编号,最后会产生一个长度为2N2N2N的序列,就成为该树的DFSDFSDFS序。

int a[N],cnt;
int dfs(int u){a[++cnt] = u;//用a数组存DFS序vis[u] = 1;for(int i = head[u]; i;i = nex[i]){int v = ver[i];if(vis[v])continue;dfs(v);}a[++cnt] = u;
}

DFSDFSDFS序的特点时:每个节点的xxx的编号在序列中恰好出现两次。设这两次出现的位置时L[x],R[x]L[x],R[x]L[x],R[x],那么闭区间[L[x],R[x]][L[x],R[x]][L[x],R[x]]就是以xxx为根的子树的DFSDFSDFS序。
dfs序可以把一棵树区间化,即可以求出每个节点的管辖区间。
对于一棵树的dfs序而言,同一棵子树所对应的一定是dfs序中连续的一段。


放一个博客。
dfs序的七个基本问题

4.树的深度

树中各个节点的深度是一种自顶向下的统计信息

起初,我们已知根节点深度是000.若节点x的深度为d[x]d[x]d[x],则它的子结点 yyy 的深度就是d[y]=d[x]+1d[y]=d[x]+1d[y]=d[x]+1


int dep[N];
void dfs(int u){vis[u] = 1;for(int i = head[u];i;i = nex[i]){int v = ver[i];if(vis[v])continue;dep[v] = dep[u]+1;//父结点 u 到子结点 v  递推 dfs(v);}
}

5.树的重心与sizesizesize

树的重心是自底向上统计的
树的重心也叫树的质心。对于一棵树n个节点的无根树,找到一个点,使得把树变成以该点为根的有根树时,最大子树的结点数最小。

【树形DP】树的重心详解+多组例题详解


int vis[N];
int Size[N];
int ans = INF;
int id;
void dfs(int u){vis[u] = 1;Size[u] = 1;//子树的大小int max_part = 0;for(int i = head[u];i;i = nex[i]){int v = ver[i];if(vis[v])continue;dfs(v);Size[u] += Size[v];max_part = max(max_part,Size[v]);//比较儿子的size因为这里是假设以u为重心}max_part = max(max_part,n-Size[u]);//n为整棵树的结点数if(max_part<ans){//更新ans = max_part;//记录重心对应的max_part的值id = u;//记录重心位置}
}

6.图的连通块划分

若在一个无向图中的一个子图中任意两个点之间都存在一条路径(可以相互到达),并且这个子图是“极大的”(不能在扩展),则称该子图是原图的一个联通块

如下代码所示,cnt是联通块的个数,v记录的是每一个点属于哪一个联通块
经过连通块划分,可以将森林划分出每一颗树,或者将图划分为各个连通块。

int cnt;
void dfs(int u){vis[u] = cnt;//这里存的是第几颗树或者是第几块连通图for(int i = head[u];i;i = nex[i]){int v = ver[i];if(vis[v])continue;dfs(v);}
}
int main()
{for(int i = 1;i<=n;++i){if(!vis[i])//如果是颗新树就往里面搜++cnt,dfs(i);}
}

二、树与图的广度优先搜索

树与图的广度优先遍历,顺便求d数组(树结点的深度/图结点的层次)。

void bfs(){memset(d,0,sizeof d);queue<int>q;q.push(1);d[1] = 1;while(q.size()){int u = q.front();q.pop();for(int i = head[u];i;i = nex[i]){int v = ver[i];if(d[v])continue;d[v] = d[u]+1;q.push(v);}}
}

广度优先遍历是一种按照层次顺序访问的方法。
它具有两个重要的性质:

  1. 在访问完所有的第i层结点后,才会访问第i+1层结点。
  2. 任意时刻,队列中只会有两个层次的结点,满足“两段性”和“单调性”。

三、拓扑排序

给定一张有向无环图,若一个序列A满足图中的任意一条边(x,y)x都在y的前面呢么序列A就是图的拓扑排序

求拓扑序的过程非常简单我们只需要不断将入度为0的点加入序列中即可
(入度:有向图中以结点x为终点的有向边的条数)
(出度:有向图中以结点x为起点的有向边的条数)
(无向图中的度:以x为端点的无向边的条数)

  1. 建立空拓扑序列A
  2. 预处理出所有入度为deg[i],起初把入度为0的点入队
  3. 取出对头节点x,并把x放入序列A中
  4. 对于从x出发的每条边(x,y),把deg[y]减1,若deg[y] = 0 ,把y加入队列中
  5. 重复3,4直到队列为空,此时A即为所求

拓扑排序可以判定有向图中是否存在环。若拓扑排序以后的A序列的长度小于图中点的长度,说明有的点没有被遍历,进而说明存在环。

int head[N],ver[N],nex[N],edge[N],tot;
int n,m;
int deg[N];
int A[N];
void add(int u,int v){ver[++tot] = v;nex[tot] = head[u];head[u] = tot;deg[v]++;//入度加1
}
int cnt;
void toposort(){//拓扑排序queue<int>q;for(int i = 1;i<=n;++i)if(deg[i] == 0)q.push(i);//步骤2,先找所有度为0的点while(q.size()){int u = q.front();q.pop();A[++cnt] = u;for(int i = head[u];i;i = nex[i]){int v = ver[i];if(--deg[v] == 0)q.push(v);}}
}
int main()
{cin>>n>>m;for(int i = 1;i <=m;++i){int x,y;scanf("%d%d",&x,&y);add(x,y);}toposort();for(int i = 1;i <=cnt;++i){printf("%d ",A[i]);}puts("");return 0;
}

AcWing 164. 可达性统计

利用拓扑排序得到的序列满足x一定在y 的前面,也就是说求完拓扑序以后倒序可以保证从所有的叶子节点开始往根走。这里要求每个点的能到达的数量,其实就是从叶子结点往上递推,有点类似最短Hamilton路径,所以用一个二进制数来状态压缩。1代表可以到达。这里可以用bitset来压缩空间个时间,而且bitset还支持count功能直接求得1的个数,这个1的个数就是这个点能到达的点的个数。

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<bitset>
#include<queue>
#include<unordered_map>
#define ls (p<<1)
#define rs (p<<1|1)
#pragma GCC optimize (2)
#pragma G++ optimize (2)
#define over(i,s,t) for(register int i = s;i <= t;++i)
#define lver(i,t,s) for(register int i = t;i >= s;--i)
//#define int __int128
using namespace std;
#undef mid
typedef long long ll;
typedef pair<int,int> PII;const int N = 30007;
const ll mod = 1e9+7;
const ll INF = 1e15+7;
const double EPS = 1e-10;
const int base = 131;int n,m;
int ver[N],nex[N],head[N],tot,cnt;
int deg[N],a[N];
bitset<N>f[N];void add(int u,int v){ver[++tot] = v;nex[tot] = head[u];head[u] = tot;deg[v]++;
}void toposort(){queue<int>q;for(int i = 1;i<=n;++i){if(deg[i] == 0)q.push(i);}while(q.size()){int u = q.front();q.pop();a[++cnt] = u;for(int i = head[u];i;i = nex[i]){int v = ver[i];if(--deg[v] == 0)q.push(v);}}
}void calc(){lver(i,cnt,0){//倒序从叶子结点开始int x = a[i];f[x][x] = 1;//自己肯定要到达自己for(int i = head[x];i;i = nex[i]){int y = ver[i];f[x] |= f[y];//求并集}}
}int main()
{scanf("%d%d",&n,&m);over(i,1,m){int x,y;scanf("%d%d",&x,&y);add(x,y);}toposort();calc();for(int i = 1;i <=n;++i){printf("%d\n",f[i].count());//输出有多少个1}return 0;
}

注:如果您通过本文,有(qi)用(guai)的知识增加了,请您点个赞再离开,如果不嫌弃的话,点个关注再走吧,日更博主每天在线答疑 ! 当然,也非常欢迎您能在讨论区指出此文的不足处,作者会及时对文章加以修正 !如果有任何问题,欢迎评论,非常乐意为您解答!( •̀ ω •́ )✧

0x21.搜索 - 树与图的遍历、拓扑排序相关推荐

  1. 大数据工作流任务调度--有向无环图(DAG)之拓扑排序

    拓扑排序(Topological Sorting) 回顾基础知识: 1.图的遍历: 图的遍历是指从图中的某一个顶点出发,按照某种搜索方法沿着图中的边对图中的所有顶点访问一次且仅访问一次.注意树是一种特 ...

  2. 家谱树 (并查集拓扑排序)

    目录 拓扑排序 Kahn. 最后附上Kahn的代码: 链式前向星做法: 其次是我用Kahn做的家谱树的代码(矩阵): 其次是我用Kahn做的家谱树的代码(链式前向星): [题目描述] 有个人的家族很大 ...

  3. 有向无环图的所有拓扑排序

    有向无环图的所有拓扑排序 对有向无环图DAG的拓扑排序是顶点的线性排序,从而使每一有向边[u,v][u,v][u,v],顶点u进来的顺序v在.如果图不是 DAG,则无法对图进行拓扑排序. 给定一个 D ...

  4. 图论--拓扑排序--判断一个图能否被拓扑排序

    拓扑排序的实现条件,以及结合应用场景,我们都能得到拓扑排序适用于DAG图(Directed Acyclic Graph简称DAG)有向无环图, 根据关系我们能得到一个线性序列,实现的方式是DFS,具体 ...

  5. 图——深度优先遍历(DFS)实现有向无环图的逆拓扑排序

    对图的深度遍历与对树的深度遍历思想类似,采用的是递归函数的办法. 如果是非连通图,则DFS遍历所有顶点即可. //Graph 图 //vertex 顶点,用一个int型变量表示//返回有向图G中顶点v ...

  6. [课程复习] 数据结构之线性表、树、图、查找、排序经典算法复习

    作者最近在复习考博,乘此机会分享一些计算机科学与技术.软件工程等相关专业课程考题,一方面分享给考研.考博.找工作的博友,另一方面也是自己今后完成这些课程的复习资料,同时也是在线笔记.基础知识,希望对您 ...

  7. 图的逆拓扑排序(回路识别)

    目录 背景 实现思路 代码 背景 在学习拓扑排序的时候,老师提出了一个问题:在逆拓扑排序算法中如何识别出回路? 总所周知,拓扑排序必须在DAG(有向无环图)中实现,也就是说如果给定的图带有回路,就无法 ...

  8. 图综合练习--拓扑排序_03 数据结构与算法 - 排序

    1. 冒泡排序 Bubble Sort 基本思想 给定一个数组,这些元素将通过相互之间的比较,按照大小顺序一个个地像气泡一样浮出水面 实现 每一轮,从头部开始,每两个元素比较大小进行交换,直到这一轮中 ...

  9. 数据结构——AOV图与算法——拓扑排序

    AOV图:以有向图中的顶点来表示活动,以有向边来表示活动之间的先后次序关系. 拓扑排序:对一个有向无环图(AOV)进行活动先后的排序方法 拓扑排序思路: 1.统计所有节点的入度 2.把所有入度为0的节 ...

最新文章

  1. 一份面向工程师的Google面试指南!GitHub 9.8万星高赞,中文版发布
  2. 使用 OpenSSL 替代 Telnet
  3. Replication的犄角旮旯(五)--关于复制identity列
  4. android 尺寸变化动画,Android ScaleAnimation类:尺寸变化动画类
  5. 亲戚(信息学奥赛一本通-T1389)
  6. JSON对象如何转化为字符串?
  7. 计算机网络科研项目申请书,科研项目申请书范文例.doc
  8. 机器学习与算法(6)--学习矢量化
  9. 集团施工企业安全生产风险管控和隐患排查治理双重预防机制数字化建设方案
  10. 婚姻中,不去表达爱,比不爱更可怕
  11. linux新建用户退格键(删除键)无法正常使用的问题总结
  12. ubuntu桌面幻灯片_如何在Ubuntu中创建墙纸幻灯片
  13. 什么叫计算机网络虫洞,虫洞:危险与希望并存
  14. 我花了五个小时的时间,将全部文章详细整理出来了,千万不要错过!
  15. node对文件的读取和修改
  16. 【CSDN软件工程师能力认证学习精选】Python网络编程(socket编程)
  17. MySQL数据完整性约束
  18. Python 把csv文件转换为excel文件
  19. 基于FPGA的啸叫检测与抑制系统设计与实现verilog
  20. Picture-In-Picture 画中画模式

热门文章

  1. 干货 | YOLOV5 训练自动驾驶数据集,并转Tensorrt,收藏!
  2. 基于点云的3D障碍物检测
  3. OpenCV中使用YOLO对象检测
  4. 实战:基于OpenCV进行长时间曝光(内含彩蛋)
  5. codeup:问题 D: 最短路径
  6. iOS 生成二维码/条形码
  7. “阿里巴巴大数据系统体系”学习笔记-纲领篇
  8. 6次课(相对和绝对路径、cd命令、创建和删除目录mkdir/rmdir、rm命令)
  9. java- ASM 字节码操控框架
  10. Altium Designer同一个工程里不同原理图导入到不同的PCB