dfs序简介

dfs序一般用于树状结构中,如图:
在这里插入图片描述

图中红色序号为每个点对应的dfs序序号,黑色序号为每个点默认的序号,我称之为节点序序号(下文同)
可见,dfs序如其名,dfs序序号是按照dfs顺序标记的,所以说给每个节点安排上dfs序序号也很简单,只要dfs的时候顺便标上就行了,dfs第多少次就给dfs到的点标为多少。

代码模板

代码如下:

//vector<int> node[N];
//int in[N],out[N];
int Time = 0; //时间戳
void dfs(int now,int fa){
     in[now]=++Time;
     num[Time]=now;//生成新的线性结结构
     for(int i=0;i<node[now].size();i++)
          if(node[now][i]!=fa) dfs(node[now][i],now);
     out[now]=Time;
}

数组讲解:
node[i] 表示二维数组,这个应该会吧,不会的话可以看看STL讲解,也可以私聊我。
in[i] 表示节点序序号为i的节点为根节点的树的开头,同时也是节点序序号为i的节点对应的dfs序序号。
out[i] 表示节点序序号为i的节点为根节点的树的结尾,必定是个树叶对应的dfs序序号。
以上面的图为例:
在这里插入图片描述

节点序序号为1的节点为根节点所对应的子树为绿框所选部分,其对应的树为dfs序序号4~ 8,因此,in[1] = 4 , out[1] = 8;
节点序序号为8的节点为根节点所对应的子树为蓝框所选部分,其对应的树为dfs序序号2~ 3,因此,in[8] = 2 , out[8] = 3;
节点序序号为6的节点为根节点所对应的子树为其本身(图中未标),其对应的树为dfs序序号7~ 7,因此,in[6] = 7 , out[6] = 7。
变量讲解:
now 代表dfs到达的当前节点;
fa 代表father,当前节点的父亲节点,主要用于循环体内部的判断;
Time 代表时间戳,什么时间戳扯这么高大上干什么? ,记录dfs的次数,dfs一次Time自加一次,其实Time记录的就是dfs序序号,用于给in数组和out数组赋值,in数组和out数组存储的就是节点的dfs序序号。
代码讲解:
说白了就是dfs板子加了几句:
dfs板子:

void dfs(int now,int fa){
  for(int i=0;i<node[now].size();i++)
    if(node[now][i]!=fa) dfs(node[now][i],now);
}

这不就是深度优先搜索嘛,只不过在每次进入dfs的时候给in数组赋上值,因为开始dfs代表着进入这棵子树了;每次dfs结束return回去的时候再给out数组赋上值,因为return回去代表子树上的节点全部访问完毕,此时Time就是出树的dfs序序号。赋值赋的就是dfs序序号。
本质讲解:
整理一下,
代码实现上,很简单,dfs加上两个赋值语句;
目的上,我们dfs的目的是对in数组和out数组进行赋值,对它们进行赋值的目的是实现节点序序号转化成dfs序序号,而为什么要将节点序序号转化成dfs序序号,就要谈谈dfs序的优势了!

dfs序的优势及用途

dfs序的主要作用就是将一个子树变成序列上的一个连续区间。
还是以上图为例,
在这里插入图片描述

绿色子树可以通过dfs序序号表示出来,4~ 8;
红色子树表示为2~ 3;
显然,可以用一连续区间表示出来任意子树。
这样的变换可以解决类似“修改树上某点,输出子树和或者最大值最小值”等问题。
常常配合线段树或树状数组。
树状数组讲解