1.树和图的存储
树是一种特殊的图。树是无环、连通图。
1.1有向图
1.1.1 邻接矩阵
g[a,b] 存储a->b
空间复杂度n^2
1.1.2邻接表
为每一个点开了一个单链表,存储可以到达的位置。
代码实现
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = N * 2;
int n;
int h[N]; //h存放n个链表的链表头
int e[M];//e存放每一个结点的值
int ne[M];//ne存放每个结点的next值
int idx;
//插入
void add(int a,int b)
{
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
int main()
{
//将所有头结点置为-1
memset(h,-1,sizeof h);
}
1.2无向图
2.树和图的遍历
2.1深度优先遍历(一条道走到黑)
void dfs(int u)//u表示当前dfs已经到哪一个点了
{
st[u] = true;//标记一下,已经被搜过了
//遍历一下u的所有初边
for(int i = h[u];i != -1;i = ne[i])
{
//j存放当前链表里面的结点对应图里面点的编号是多少
int j = e[i];
if(!st[j]) dfs(j);//没有搜索到,继续搜
}
}
例题:树的重心 (难)
题意:
最优解为4.
代码
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010, M = N * 2;
int n,m;
int h[N]; //h存放n个链表的链表头
int e[M];//e存放每一个结点的值
int ne[M];//ne存放每个结点的next值
int idx;
bool st[N];//存储哪些点已经被遍历过
int ans = N;//全局答案,最小的最大值
//插入
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)//u表示当前dfs已经到哪一个点了
{
st[u] = true;//标记一下访问过u结点
int sum = 1;//存储 删掉某个节点之后,最大的连通子图节点数
int res = 0;//存储 以u为根的树 的节点数, 包括u,如图中的4号节点
//遍历一下u的所有初边
for(int i = h[u];i != -1;i = ne[i])
{
//j存放当前链表里面的结点对应图里面点的编号是多少
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(ans,res);
return sum;
}
int main()
{
scanf("%d", &n);//表示树的结点数
//将所有头结点置为-1
memset(h,-1,sizeof h);
// 题目接下来会输入,n-1行数据,
// 树中是不存在环的,对于有n个节点的树,必定是n-1条边
for (int i = 0; i < n - 1; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b), add(b, a);
}
dfs(1);//可以任意选定一个节点开始 u<=n
printf("%d\n", ans);
return 0;
dfs(1);
}
2.2宽度优先遍历(按层搜)
题目:图中点的层次
思路
代码
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;//邻接表
int d[N];
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;
d[1] = 0;
q.push(1);
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()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
}
cout << bfs() << endl;
return 0;
}
3.图的拓补序列(针对有向图)
所有边都是从前指向后的。
- 入度
- 出度
一个有向无环图,至少存在一个入度为0的点。
代码
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 100010;
int n, m;
int h[N], e[N], ne[N], idx;//邻接表
int d[N];//入度
int q[N];//队列
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
bool topsort()
{
//队头,队尾
int hh = 0, tt = -1;
//将所有入度为0的点插入队列中
for (int i = 1; i <= n; i ++ )
if (!d[i])
q[ ++ tt] = i;
//队列不空
while (hh <= tt)
{
int t = q[hh ++ ];
for (int i = h[t]; i != -1; i = ne[i])
{
int j = e[i];
if (-- d[j] == 0)//入度--
q[ ++ tt] = j;
}
}
//判断是否所有点都入队
return tt == n - 1;
}
int main()
{
scanf("%d%d", &n, &m);
memset(h, -1, sizeof h);
for (int i = 0; i < m; i ++ )
{
int a, b;
scanf("%d%d", &a, &b);
add(a, b);
d[b] ++ ;//入度数
}
if (!topsort()) puts("-1");
else
{
for (int i = 0; i < n; i ++ ) printf("%d ", q[i]);
puts("");
}
return 0;
}