树与图的存储
- 数是一种特殊的图(无环连通图)
- 对于无向图中的边ab,存储两条有向边a->b, b->a
- 因此我们可以只考虑有向图的存储
邻接矩阵存储
- 二维数组
g[a][b]来存储边a->b- 如果有权重,里面就存权重,否则就存布尔值代表有没有这条边
- 无法存储重边,有重边只能保留一条(最短路的话就存权重最小的)
- 空间复杂度n^2^,比较浪费空间,比较适合存储稠密图
邻接表存储
- C++
// 对于每个点k,开一个单链表,存储k所有可以走到的点。h[k]存储这个单链表的头结点
int h[N], e[N], ne[N], idx;
// 添加一条边a->b
void add(int a, int b)
{
e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}
// 初始化
idx = 0;
memset(h, -1, sizeof h);
- Java
public static final N = 100010;
public static final M = N * 2;
public static int[] h = new int[N];
public static int[] e = new int[M];
public static int[] ne = new int[M];
public static int idx;
public static void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
//初始化
idx = 0;
Arrays.fill(h, -1);
树与图的遍历
- 时间复杂度 O(n+m),n表示点数,m表示边数
深度优先遍历
-
树与图一般只需要搜索一次,所以不需要回溯
-
C++
int dfs(int u)
{
st[u] = true; // st[u] 表示点u已经被遍历过
for (int i = h[u]; i != -1; i = ne[i])
{
int j = e[i];
if (!st[j]) dfs(j);
}
}
- Java
//布尔数组st表示当前点是否被搜过
public static boolean[] st = new boolean[N];
public static int dfs(int u) {
st[u] = true; //先将当前节点设为true,表示已经被搜过
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
if (st[j]) {
dfs(j);
}
}
}
宽度优先遍历
- C++
queue<int> q;
st[1] = true; // 表示1号点已经被遍历过
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 (!st[j])
{
st[j] = true; // 表示点j已经被遍历过
q.push(j);
}
}
}
- Java
public static boolean[] st = new boolean[N];
public static void bfs() {
ArrayDeque<Integer> q = new ArrayDeque<>();
st[1] = true; //表示1号点已经被遍历过
q.offer(1); //将1号点插入队尾
while (q.size() != 0) {
int t = q.poll(); //弹出队头
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
if (!st[j]) {
st[j] = true; //表示j号点已经被遍历过
q.offer(j); //将j号点插入队尾
}
}
}
}
练习
01 树的重心
- 题目
- 题解
import java.io.*;
import java.util.*;
public class Main {
public static final int N = 100010;
public static final int M = N * 2;
public static int[] h = new int[N];
public static int[] e = new int[M];
public static int[] ne = new int[M];
public static boolean[] st = new boolean[N];
public static int n, idx;
public static int ans = N;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
n = Integer.parseInt(br.readLine());
//初始化
idx = 0;
Arrays.fill(h, -1);
//n-1条数据
int m = n - 1;
while (m-- > 0) {
String[] str1 = br.readLine().split(" ");
int a = Integer.parseInt(str1[0]);
int b = Integer.parseInt(str1[1]);
//无向图
add(a, b);
add(b, a);
}
dfs(1);
pw.println(ans);
br.close();
pw.close();
}
public static void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
//dfs返回以当前节点为根的子树的节点个数
public static int dfs(int u) {
//先将当前节点设为true,表示已经被搜过
st[u] = true;
//sum是dfs的返回值 表示以当前节点为根的子树的节点个数,先算上自己
int sum = 1;
//res表示将这个节点删除后,剩余各个连通块中节点数量的最大值
int res = 0;
for (int i = h[u]; i != -1; i = ne[i]) {
int j = e[i];
//如果当前节点的子节点没有被搜过继续往下搜
if (!st[j]) {
//返回的s是以 当前节点的子节点 为根节点 的 子树 的节点个数
int s = dfs(j);
//其中s也是将当前节点删除后,形成的一个连通块的节点个数,做一下比较,取最大值
res = Math.max(res, s);
//把s加上
sum += s;
}
}
//这时候就已经把以 当前节点的所有子节点 为根节点 的子树都遍历了一遍
//sum也就算出来了
//还要和当前节点向上的连通块的节点个数进行比较,取最大值
res = Math.max(res, n - sum);
//取最大值的最小值
ans = Math.min(ans, res);
return sum;
}
}
01 图中点的层次
- 题目
- 题解
import java.io.*;
import java.util.*;
public class Main {
public static final int N = 100010;
public static int[] h = new int[N];
public static int[] e = new int[N];
public static int[] ne = new int[N];
//存从1号点到i号点的距离
public static int[] d = new int[N];
public static int idx, n, m;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
String[] str1 = br.readLine().split(" ");
n = Integer.parseInt(str1[0]);
m = Integer.parseInt(str1[1]);
//初始化图
idx = 0;
Arrays.fill(h, -1);
while (m-- > 0) {
String[] str2 = br.readLine().split(" ");
int a = Integer.parseInt(str2[0]);
int b = Integer.parseInt(str2[1]);
add(a, b);
}
pw.println(bfs());
br.close();
pw.close();
}
public static void add(int a, int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx++;
}
public static int bfs() {
//初始化d数组
Arrays.fill(d, -1);
d[1] = 0;
ArrayDeque<Integer> q = new ArrayDeque<>();
q.offer(1);
while (q.size() != 0) {
int t = q.poll();
for (int i = h[t]; i != -1; i = ne[i]) {
int j = e[i];
//只有当当前节点没有到过时才继续走
if (d[j] == -1) {
d[j] = d[t] + 1;
q.offer(j);
}
}
}
return d[n];
}
}