图论Graph theory 基础
研究由点和边组成的数学模型
应用:
交通运输
社交网络
互联网
工作安排
脑区活动
程序状态执行
图的分类
图的连通性
简单图(Simple Graph)
图的表示
邻接表适合表示稀疏图(Sparse Graph)
邻接矩阵适合表示稠密图(Dense Graph)
邻接矩阵
表示无向图
表示有向图
邻接表
表示无向图
表示有向图
图的实现
邻接矩阵
public class DenseGraph {
private int n;// 节点数
private int m;// 边数
private boolean directed;//是否为有向图
private boolean[][] g;// 图的具体数据
public DenseGraph(int n, boolean directed) {
this.n = n;
this.m = 0;
this.directed = directed;
g = new boolean[n][n];
}
public int getNodesCount() {
return n;
}
public int getEdgesCount(){
return m;
}
public void addEdge(int v, int w) {
if (v < 0 || v >= n || w < 0 || w >= n) {
return;
}
if (hasEdge(v, w)) {
return;
}
g[v][w] = true;
if (!directed) {
g[w][v] = true;
}
m++;
}
public boolean hasEdge(int v, int w) {
if (v < 0 || v >= n || w < 0 || w >= n) {
return false;
}
return g[v][w];
}
}
邻接表
public class SparseGraph {
private int n, m;
private boolean directed;
private List<List<Integer>> g;
public SparseGraph(int n, boolean) {
if (n < 0) {
return;
}
this.n = n;
this.m = 0;
this.directed = directed;
g = (ArrayList<Integer>[])new ArrayList[n];
}
public int getNodesCount() {
return n;
}
public int getEdgesCount() {
return m;
}
public boolean hasEdge(int v, int w) {
if (v < 0 || v >= n || w < 0 || w >= n) {
return false;
}
int n = g[v].size();
for (int i = 0; i < n; i++) {
if(g[v].get(i) == w) {
return true;
}
}
return false;
}
public void addEdge(int v, int w) {
if (v < 0 || v >= n || w < 0 || w >= n) {
return;
}
//如果不允许平行边,需要加这样的判断
if (hasEdge(v, w)) {
return;
}
//如果允许平行边,注释掉上述判断
g[v].add(w);
if (v != w && !directed) {
g[w].add(v);
}
m++;
}
}
遍历边
返回一个节点的所有邻边
对于稀疏图来说
返回当前行即可
public Iterable<Integer> adj(int v) {
if (v < 0 || v >= n) {
return new ArrayList<Integer>();
}
return g[v];
}
对于稠密图来说
需要遍历一下,代码如下:
public Iterable<Integer> adj(int v) {
List<Integer> res = new ArrayList<>();
if (v < 0 || v >= n) {
return res;
}
for (int i = 0; i < n; i++) {
if (g[v][i]) {
res.add(i);
}
}
return res;
}
边的遍历对图的遍历至关重要!
阶段重构
抽象一个图的接口
public interface Graph {
public int getNodesCount();
public int getEdgesCount();
public void addEdge(int v, int w);
public boolean hasEdge(int v, int w);
public Iterable<Integer> adj(int v);
}
将DenseGraph和SparseGraph修改为实现Graph接口
图的遍历
深度优先遍历
从一个节点开始,不断向下,直到无法进行为止,由于图可能有环,因此需要记录遍历过程中的点。
求一个图的联通分量
以求一个图的联通分量来展示图的深度优先遍历
//求无权图的联通分量
public class Component {
private Graph g;
private boolean[] visited;// 记录dfs的过程中节点是否被访问
private int ccount;// 记录联通分量个数
private int[] id;// 每个节点所对应的联通分量标记
public Component(Graph g) {
this.g = g;
int n = g.getNodesCount();
visited = new boolean[n];
id = new int[n];
ccount = 0;
//初始化
for (int i = 0; i < n; i++) {
visited[i] = false;
id[i] = -1;
}
//完成联通分量求解
for (int i = 0; i < n; i++) {
if (!visited[i]) {
dfs(i);
ccount++;
}
}
}
private void dfs(int i) {
visited[i] = true;
id[i] = ccount;
for (int j : g.adj(v)) {
if (!visited[j]) {
dfs(j);
}
}
}
//返回联通分量
public int count() {
return ccount;
}
//判断两点是否联通
public boolean isConnected(int v, int w) {
if (g == null || g.getNodesCount() == 0) {
return false;
}
int n = g.getNodesCount();
if (v < 0 || v >= n || w < 0 || w >= n) {
return false;
}
return id[v] == id[w];
}
}
求两点的路径
public class Path {
private Graph g;
private boolean[] visited;
private int s;
private int[] from;
public Path(Graph g, int s) {
this.g = g;
this.s = s;
int n = g.getNodesCount();
visited = new boolean[n];
from = new int[n];
for (int i = 0; i < n; i++) {
visited[i] = false;
from[i] = -1;
}
dfs(s);
}
private void dfs(int i) {
visited[i] = true;
for (int j : g.adj()) {
if (!visited[i]) {
from[j] = i;
dfs(j);
}
}
}
public boolean hasPath(int w) {
if (w < 0 || w >= g.getNodesCount()) {
return false;
}
return visited[w];
}
public List<Integer> path(int w) {
List<Integer> res = new ArrayList<>();
if (w < 0 || w >= g.getNodesCount()) {
return res;
}
Stack<Integer> s = new Stack<>();
int p = w;
while (p != -1) {
s.push(p);
p = from[p];
}
while (!s.isEmpty()) {
res.add(s.pop());
}
return res;
}
}
深度遍历复杂度
稀疏图O(V+E)
稠密图O(V^2)
深度优先遍历对有向图依旧有效
广度优先遍历
又称为层序遍历,以两点间的最短路径为例
public class ShortestPath {
private Graph g;
private int s;
private boolean[] visited;
private int[] from;
private int[] order;
public ShortestPath(Graph g, int s) {
this.g = g;
this.s = s;
int n = g.getNodesCount();
from = new int[n];
visited = new boolean[n];
order = new int[n];
for (int i = 0; i < n; i++) {
from[i] = -1;
visited[i] = false;
order[i] = -1;
}
Queue<Integer> queue = new LinkedList<>();
queue.offer(s);
visited[s] = true;
order[s] = 0;
while (!queue.isEmpty()) {
int v = queue.poll();
for (int i : g.adj()) {
if (!visited[i]) {
queue.offer(i);
visited[i] = true;
from[i] = v;
order[i] = order[v] + 1;
}
}
}
}
public boolean hasPath(int w) {
if (w < 0 || w >= g.getNodesCount()) {
return false;
}
return visited[w];
}
public List<Integer> path(int w) {
List<Integer> res = new ArrayList<>();
if (w < 0 || w >= g.getNodesCount()) {
return res;
}
Stack<Integer> s = new Stack<>();
int p = w;
while (p != -1) {
s.push(p);
p = from[p];
}
while (!s.isEmpty()) {
res.add(s.pop());
}
return res;
}
public int length(int w) {
if (w < 0 || w >= g.getNodesCount()) {
return -1;
}
return order[w];
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。