java树深度优先遍历 树深度优先遍历算法_图论


(数组建立邻接表) 树的dfs

//邻接表
 int h[N], e[N * 2], ne[N * 2], idx;
void add(int a, int b) {
 e[idx] = b, ne[idx] = h[a], h[a] = idx++;
 }
 树的bfs模板
 // 需要标记数组st[N], 遍历节点的每个相邻的便
 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);
 }
 }
 }


本题的本质是树的dfs, 每次dfs可以确定以u为重心的最大连通块的节点数,并且更新一下ans。

也就是说,dfs并不直接返回答案,而是在每次更新中迭代一次答案。

这样的套路会经常用到,在 树的dfs 题目中

详细注释

#include 
 #include 
 #include 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++;
 }// 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) {
 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;

}

本题的邻接表存储结构中,有两个容易混淆的地方,一个是节点的编号,一个是节点的下标。
节点的编号是指上图所画的树中节点的值,范围是从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。