树和图的遍历与搜索
1.树的重心 acwing 846

1 #include <iostream> 2 #include <algorithm> 3 #include <cstring> 4 5 using namespace std; 6 7 const int N = 1e5 + 10; //数据范围是10的5次方 8 const int M = 2 * N; //以有向图的格式存储无向图,所以每个节点至多对应2n-2条边 9 10 int h[N]; //邻接表存储树,有n个节点,所以需要n个队列头节点 11 int e[M]; //存储元素 12 int ne[M]; //存储列表的next值 13 int idx; //单链表指针 14 int n; //题目所给的输入,n个节点 15 int ans = N; //表示重心的所有的子树中,最大的子树的结点数目 16 17 bool st[N]; //记录节点是否被访问过,访问过则标记为true 18 19 //a所对应的单链表中插入b a作为根 20 void add(int a, int b) { 21 e[idx] = b, ne[idx] = h[a], h[a] = idx++; 22 } 23 24 // dfs 框架 25 /* 26 void dfs(int u){ 27 st[u]=true; // 标记一下,记录为已经被搜索过了,下面进行搜索过程 28 for(int i=h[u];i!=-1;i=ne[i]){ 29 int j=e[i]; 30 if(!st[j]) { 31 dfs(j); 32 } 33 } 34 } 35 */ 36 37 //返回以u为根的子树中节点的个数,包括u节点 38 int dfs(int u) { 39 int res = 0; //存储 删掉某个节点之后,最大的连通子图节点数 40 st[u] = true; //标记访问过u节点 41 int sum = 1; //存储 以u为根的树 的节点数, 包括u,如图中的4号节点 42 43 //访问u的每个子节点 44 for (int i = h[u]; i != -1; i = ne[i]) { 45 int j = e[i]; 46 //因为每个节点的编号都是不一样的,所以 用编号为下标 来标记是否被访问过 47 if (!st[j]) { 48 int s = dfs(j); // u节点的单棵子树节点数 如图中的size值 49 res = max(res, s); // 记录最大联通子图的节点数 50 sum += s; //以j为根的树 的节点数 51 } 52 } 53 54 //n-sum 如图中的n-size值,不包括根节点4; 55 res = max(res, n - sum); // 选择u节点为重心,最大的 连通子图节点数 56 ans = min(res, ans); //遍历过的假设重心中,最小的最大联通子图的 节点数 57 return sum; 58 } 59 60 int main() { 61 memset(h, -1, sizeof h); //初始化h数组 -1表示尾节点 62 cin >> n; //表示树的结点数 63 64 // 题目接下来会输入,n-1行数据, 65 // 树中是不存在环的,对于有n个节点的树,必定是n-1条边 66 for (int i = 0; i < n - 1; i++) { 67 int a, b; 68 cin >> a >> b; 69 add(a, b), add(b, a); //无向图 70 } 71 72 dfs(1); //可以任意选定一个节点开始 u<=n 73 74 cout << ans << endl; 75 76 return 0; 77 }
2.拓扑排序

1 #include <iostream> 2 #include <cstring> 3 #include <algorithm> 4 using namespace std; 5 const int N = 100010; 6 int e[N], ne[N], idx;//邻接表存储图 7 int h[N]; 8 int q[N], hh = 0, tt = -1;//队列保存入度为0的点,也就是能够输出的点, 9 int n, m;//保存图的点数和边数 10 int d[N];////保存各个点的入度 11 12 void add(int a, int b){ 13 e[idx] = b, ne[idx] = h[a], h[a] = idx++; 14 } 15 16 void topsort(){ 17 for(int i = 1; i <= n; i++){//遍历一遍顶点的入度。 18 if(d[i] == 0)//如果入度为 0, 则可以入队列 19 q[++tt] = i; 20 } 21 while(tt >= hh){//循环处理队列中点的 22 int a = q[hh++]; 23 for(int i = h[a]; i != -1; i = ne[i]){//循环删除 a 发出的边 24 int b = e[i];//a 有一条边指向b 25 d[b]--;//删除边后,b的入度减1 26 if(d[b] == 0)//如果b的入度减为 0,则 b 可以输出,入队列 27 q[++tt] = b; 28 } 29 } 30 if(tt == n - 1){//如果队列中的点的个数与图中点的个数相同,则可以进行拓扑排序 31 for(int i = 0; i < n; i++){//队列中保存了所有入度为0的点,依次输出 32 cout << q[i] << " "; 33 } 34 } 35 else//如果队列中的点的个数与图中点的个数不相同,则可以进行拓扑排序 36 cout << -1;//输出-1,代表错误 37 } 38 39 40 int main(){ 41 cin >> n >> m;//保存点的个数和边的个数 42 memset(h, -1, sizeof h);//初始化邻接矩阵 43 while (m -- ){//依次读入边 44 int a, b; 45 cin >> a >> b; 46 d[b]++;//顶点b的入度+1 47 add(a, b);//添加到邻接矩阵 48 } 49 topsort();//进行拓扑排序 50 return 0; 51 }

1 #include<bits/stdc++.h> 2 using namespace std; 3 4 int n,m; 5 const int N =100010; 6 int h[N],q[N],d[N]; 7 int e[N],ne[N],idx; 8 9 void add(int a,int b) 10 { 11 e[idx] = b; 12 ne[idx] = h[a]; 13 h[a] = idx ++; 14 } 15 16 bool topsort() 17 { 18 int hh = 0, tt = -1; 19 20 // d[i] 存储点i的入度 21 for (int i = 1; i <= n; i ++ ) //遍历所有的点 22 if (!d[i]) //把所有入度不为0的点插到队列中去 23 q[ ++ tt] = i; 24 25 while (hh <= tt) 26 { 27 int t = q[hh ++ ]; //取出队头 28 29 for (int i = h[t]; i != -1; i = ne[i]) //拓展队头元素 30 { 31 int j = e[i]; //找到初边 32 if (-- d[j] == 0) // 入度 -- 33 q[ ++ tt] = j; //j 编入队列 34 } 35 } 36 // 如果所有点都入队了,说明存在拓扑序列;否则不存在拓扑序列。 37 return tt == n - 1; 38 } 39 40 41 int main() 42 { 43 cin >> n >> m; 44 45 memset(h, -1, sizeof h); 46 47 for (int i = 0; i < m; i ++ ) 48 { 49 int a,b; 50 cin >> a >> b; 51 add(a,b); 52 d[b] ++; 53 } 54 55 if(topsort()) 56 { 57 for (int i = 0; i < n; i ++ ) printf("%d ",q[i]); 58 puts(""); 59 } 60 else puts("-1"); 61 62 return 0; 63 }