算法刷题:图论(9.23,持续更)
目录
基础知识
- 点:顶点、邻接节点
- 边:有向边、无向边、加权边
- 度:入度、出度、无向边的度
- 环:环、自环(glist[i]中有i)
- 连通性:连通图、不连通
有向图
顶点类
class Vertex {
int id;
List<Vertex> neighbors;
// Vertex[] neighbors;
}
邻接表
// int值表示 顶点id
// 嵌套int列表
List<List<Integer>> glist;
// int列表数组
List<Integer>[] glist;
// 顶点列表
List<Vertex> glist;
邻接表有两个层级:
- 外层 - 顶点列表
- 用一个列表存储顶点
- 内层 - 邻居列表
- 每个顶点对应个邻居列表
邻接矩阵
// boolean 方阵
boolean [][] gmtx = new [n][n];
- 每一个有向边都是矩阵的元素
- 可以快速判断两个点是否邻接,但是费空间;
入度、出度
指向当前节点的箭头数、从当前节点指出的箭头数
有向加权图
邻接表:
- 顶点类实现
class Vertex {
int id;
int val;
Vertex [] neighbors;
}
List<Vertex> glist;
- 直接存储:
// int数组的列表的数组
List<int[]>[] glist;
// int数组的列表的列表
List<List<int[]>> glist;
邻接矩阵:
int [][] gmtx = new [n][n];
无向图(双向图)
所有边的两边节点,都变成互相指向
图的遍历
系统栈 dfs 或者队列 bfs
图中包含 不连通子图 时的遍历思路:
// 遍历非连通图
for(int v = 0; v < g.length; v++){
dfs[g, v];
// if(!vst[v]) dfs(g, v);
}
题目
DAG所有可能的路径
时间 1ms,击败100%
class Solution {
List<Integer> res;
List<List<Integer>> ans;
public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
res = new ArrayList<>();
ans = new ArrayList<>();
dfs(graph, 0);
return ans;
}
public void dfs(int [][] graph, int vid){
res.add(vid);
if(vid == graph.length - 1)
ans.add(new ArrayList<>(res));
for(int v : graph[vid])
dfs(graph, v);
res.remove(res.size() - 1);
}
}
判断二分图
dfs解法
class Solution {
// 记录 当前图 是否是 二分图
private boolean isBip = true;
private boolean [] clr;
private boolean [] vst;
public boolean isBipartite(int[][] graph) {
clr = new boolean[graph.length];
vst = new boolean[graph.length];
// dfs(graph, 0);
for(int i = 0; i < graph.length; i++){
dfs(graph, i);
}
return isBip;
}
private void dfs(int [][] graph, int vid){
// 已经判定不是二分图,不进行递归
if(!isBip) return;
vst[vid] = true;
for(int v : graph[vid]){
if(!vst[v]){
// dfs(graph, v);
clr[v] = !clr[vid];
dfs(graph, v);
} else {
if(clr[v] == clr[vid]){
isBip = false;
return;
}
}
}
}
}
bfs解法
class Solution {
// 记录 当前图 是否是 二分图
private boolean isBip = true;
private boolean [] clr;
private boolean [] vst;
public boolean isBipartite(int[][] graph) {
clr = new boolean[graph.length];
vst = new boolean[graph.length];
// dfs(graph, 0);
for(int v = 0; v < graph.length; v++){
// if(!vst[v]) dfs(graph, v);
// bfs(graph, v);
if(!vst[v]) bfs(graph, v);
}
return isBip;
}
private void bfs(int [][] graph, int vid){
Deque<Integer> dq = new ArrayDeque<>();
dq.offer(vid);
while(!dq.isEmpty() && isBip){
int v = dq.poll();
vst[v] = true;
for(int w : graph[v]){
// dq.offer(w);
if(!vst[w]){
clr[w] = !clr[v];
dq.offer(w);
} else {
if(clr[w] == clr[v])
isBip = false;
}
}
}
}
环检测 / 拓扑排序
走了一路,走回曾走过的路
课程表
dfs解法 (3ms)
class Solution {
boolean [] vst;
boolean [] path;
boolean isCycle = false;
public boolean canFinish(int numCourses, int[][] prerequisites) {
// 在判断有没有环之前
// 先把 图邻接表建出来
List<Integer>[] glist = buildGraph(numCourses, prerequisites);
// 对邻接表进行 dfs 遍历
// 如果一层递归的顶点的孩子已经访问过,就是成环
vst = new boolean[numCourses];
path = new boolean[numCourses];
for(int i = 0; i < glist.length; i++)
dfs(glist, i);
return !isCycle;
}
List<Integer>[] buildGraph(int cnum, int [][] pr){
// 创建容器
List<Integer>[] glist = new LinkedList[cnum];
for(int i = 0; i < cnum; i++)
glist[i] = new LinkedList<>();
// 向 邻接表容器 添加 邻接值
for(int [] edge : pr){
int to = edge[0], from = edge[1];
glist[from].add(to);
} return glist; // 返回邻接表
}
void dfs(List<Integer>[] glist, int id){
if(isCycle) return;
// 一路上,已过了的这个点
if(path[id]) {
isCycle = true;
return;
} // 防止重复遍历
if(vst[id]) return;
path[id] = true;
vst[id] = true;
for(int v : glist[id])
dfs(glist, v);
path[id] = false;
}
}
课程表 II
dfs拓扑排序(5ms)
class Solution {
public int[] findOrder(int numCourses, int[][] prerequisites) {
List<Integer>[] glist = buildGraph(numCourses, prerequisites);
vst = new boolean[numCourses];
path = new boolean[numCourses];
res = new LinkedList<>();
for(int i = 0; i < glist.length; i++)
dfs(glist, i);
if(isCycle) return new int[0];
int [] ans = new int [numCourses];
for(int i = 0; i < numCourses; i++)
ans[i] = res.get(i);
return ans;
}
boolean [] vst;
boolean [] path;
boolean isCycle = false;
LinkedList<Integer> res;
List<Integer>[] buildGraph(int cnum, int [][] pr){
List<Integer>[] glist = new LinkedList[cnum];
for(int i = 0; i < cnum; i++)
glist[i] = new LinkedList<>();
for(int [] edge : pr){
int to = edge[0], from = edge[1];
glist[from].add(to);
} return glist;
}
void dfs(List<Integer>[] glist, int id){
if(isCycle) return;
if(path[id]) {
isCycle = true;
return;
}
if(vst[id]) return;
path[id] = true;
vst[id] = true;
for(int v : glist[id])
dfs(glist, v);
res.addFirst(id);
path[id] = false;
}
}