一、无向图Graph数据类型:
import java.util.Scanner;
public class Graph
{
private final int V;
private int E;
private Bag<Integer>[] adj;
public Graph(int v)
{
this.V = v;
this.E = 0;
adj = (Bag<Integer>[])new Bag[v];
for (int i = 0; i < v; i++)
adj[i] = new Bag<Integer>();
}
public Graph(In in)
{
this(in.readInt());
int E = in.readInt();
for (int i = 0; i < E; i++)
{
int v = in.readInt();
int w = in.readInt();
addEdge(v, w);
}
}
public int V()
{
return V;
}
public int E()
{
return E;
}
public void addEdge(int v, int w)
{
adj[v].add(w);
adj[w].add(v);
E++;
}
public Iterable<Integer> adj(int v)
{
return adj[v];
}
//常见图操作
public static int degree(Graph G, int v)
{
int count = 0;
for (int w : G.adj(v)) count++;
return count;
}
public static int maxDegree(Graph G)
{
int max = 0;
for (int v= 0; v < G.V(); v++)
{
if (degree(G,v) > max)
max = degree(G, v);
}
return max;
}
public static double avgDgree(Graph G)
{
return G.E() * 2 / G.V();
}
public static int numberOfSelfLoops(Graph G)
{
int count = 0;
for (int v = 0; v < G.V(); v++)
for (int w : G.adj(v))
if (v == w) count++;
return count / 2;
}
}
二、使用深度优先搜索查找图中路径:
public class DepthFirstPaths
{
private boolean[] marked;
private int[] edgTo;
private final int s;
public DepthFirstPaths(Graph G, int s)
{
this.s = s;
marked = new boolean[G.V()];
edgTo = new int[G.V()];
dfs(G, s);
}
private void dfs(Graph G, int v)
{
marked[v] = true;
for (int w : G.adj(v))
if (!marked[w])
{
edgTo[w] = v;
dfs(G, w);
}
}
public boolean hasPathTo(int v)
{return marked[v];}
public Iterable<Integer> pathTo(int v)
{
if (!hasPathTo(v)) return null;
Stack<Integer> path = new Stack<Integer>();
for (int x = v; x != s; x = edgTo[x])
{
path.push(x);
}
path.push(s);
return path;
}
}
三、使用广度搜索查找图中路径:
public class BreadthFirstPaths
{
private boolean[] marked;
private int[] edgTo;
private final int s;
public BreadthFirstPaths(Graph G, int s)
{
this.s = s;
marked = new boolean[G.V()];
edgTo = new int[G.V()];
bfs(G, s);
}
private void bfs(Graph G, int s)
{
Queue<Integer> queue = new Queue<Integer>();
marked[s] = true;
queue.enqueue(s);
while (!queue.isEmpty())
{
int v = queue.dequeue();
for (int w : G.adj(v))
{
if (!marked[w])
{
edgTo[w] = v;
marked[w] = true;
queue.enqueue(w);
}
}
}
}
public boolean hasPathTo(int v)
{return marked[v];}
public Iterable<Integer> pathTo(int v)
{
if (!hasPathTo(v)) return null;
Stack<Integer> path = new Stack<Integer>();
for (int x = v; x != s; x = edgTo[x])
{
path.push(x);
}
path.push(s);
return path;
}
}
四、使用深度优先找出图中连通分量:
public class CC
{
private boolean[] marked;
private int[] id;
private int count;
public CC(Graph G)
{
marked = new boolean[G.V()];
id = new int[G.V()];
for (int v = 0; v < G.V(); v++)
{
if (!marked[v])
{
dfs(G, v);
count++;
}
}
}
private void dfs(Graph G, int s)
{
marked[s] = true;
id[s] = count;
for (int v : G.adj(s))
{
if (!marked[v])
dfs(G, v);
}
}
public boolean connected(int v, int w)
{
return id[v] == id[w];
}
public int count()
{return count;}
public int id(int v)
{return id[v];}
}
五、G是无环图么:
public class Cycle
{
private boolean[] marked;
private boolean hasCycle;
public Cycle(Graph G)
{
marked = new boolean[G.V()];
for (int s = 0; s < G.V(); s++)
{
if (!marked[s])
{
dfs(G, s, s);
}
}
}
private void dfs(Graph G, int v, int u)
{
marked[v] = true;
for (int w : G.adj(v))
if (!marked[w])
{
dfs(G, w, v);
}
else if (w != v) hasCycle = true;
}
public boolean hasCycle()
{return hasCycle;}
}
六、图G是二分图么?
public class TwoColor
{
private boolean[] marked;
private boolean[] color;
private boolean isTwoColor = true;
public TwoColor(Graph G)
{
marked = new boolean[G.V()];
color = new boolean[G.V()];
for (int v = 0; v < G.V(); v++)
{
if (!marked[v])
{
dfs(G, v);
}
}
}
private void dfs(Graph G, int s)
{
marked[s] = true;
for (int v : G.adj(s))
{
if (!marked[v])
{
color[v] = !color[s];
dfs(G, v);
}
else if (color[v] == color[s]) isTwoColor = false;
}
}
public boolean isTwoColor()
{
return isTwoColor;
}
}
七、有向图类型:
public class Digraph
{
private final int V;
private int E;
private Bag<Integer>[] adj;
public Digraph(int v)
{
this.V = v;
this.E = 0;
adj = (Bag<Integer>[])new Bag[v];
for (int i = 0; i < v; i++)
adj[i] = new Bag<Integer>();
}
public Digraph(In in)
{
this(in.readInt());
int E = in.readInt();
for (int i = 0; i < E; i++)
{
int v = in.readInt();
int w = in.readInt();
addEdge(v, w);
}
}
public int V()
{
return V;
}
public int E()
{
return E;
}
public void addEdge(int v, int w)
{
adj[v].add(w);
E++;
}
public Iterable<Integer> adj(int v)
{
return adj[v];
}
public Digraph reverse()
{
Digraph R = new Digraph(V);
for (int v = 0; v < V; v++)
for (int w : adj(v))
{
R.addEdge(w, v);
}
return R;
}
//常见图操作
public static int degree(Graph G, int v)
{
int count = 0;
for (int w : G.adj(v)) count++;
return count;
}
public static int maxDegree(Graph G)
{
int max = 0;
for (int v= 0; v < G.V(); v++)
{
if (degree(G,v) > max)
max = degree(G, v);
}
return max;
}
public static double avgDgree(Graph G)
{
return G.E() * 2 / G.V();
}
public static int numberOfSelfLoops(Graph G)
{
int count = 0;
for (int v = 0; v < G.V(); v++)
for (int w : G.adj(v))
if (v == w) count++;
return count / 2;
}
}
八、有向图的可达性:
public class DirectedDFS
{
private boolean[] marked;
public DirectedDFS(Digraph G, int s)
{
marked = new boolean[G.V()];
dfs(G, s);
}
public DirectedDFS(Digraph G, Iterable<Integer> sources)
{
marked = new boolean[G.V()];
for (int s : sources)
if (!marked[s])
dfs(G, s);
}
private void dfs(Digraph G, int s)
{
marked[s] = true;
for (int v : G.adj(s))
if (!marked[v])
dfs(G, v);
}
public boolean marked(int v)
{
return marked[v];
}
public static void main(String[] args)
{
Digraph G = new Digraph(new In(args[0]));
Bag<Integer> sources = new Bag<Integer>();
for (int i = 1; i < args.length; i++)
sources.add(Integer.parseInt(args[i]));
DirectedDFS reachable = new DirectedDFS(G, sources);
for (int v = 0; v < G.V(); v++)
if (reachable.marked(v)) StdOut.print(v + " ");
StdOut.println();
}
}
九、寻找有向环:
public class DirectedCycle
{
private boolean[] marked;
private boolean[] onStack;
private Stack<Integer> cycle;
private int[] edgeTo;
public DirectedCycle(Digraph G)
{
marked = new boolean[G.V()];
onStack = new boolean[G.V()];
edgeTo = new int[G.V()];
for (int v = 0; v < G.V(); v++)
if (!marked[v])
dfs(G, v);
}
private void dfs(Digraph G, int v)
{
marked[v] = true;
onStack[v] = true;
for (int w : G.adj(v))
if (hasCycle()) return;
else if(!marked[w])
{
edgeTo[w] = v;
dfs(G, w);
}
else if (onStack[w])
{
cycle = new Stack<Integer>();
for (int x = v; x != w; x = edgeTo[x])
cycle.push(x);
cycle.push(w);
cycle.push(v);
}
onStack[v] = false;
}
public boolean hasCycle(Digraph G)
{
return cycle != null;
}
public Iterable<Integer> cycle()
{
return cycle;
}
}
十、有向图基于深度优先搜索的顶点排序:
public class DepthFirstOrder
{
private boolean[] marked;
private Queue<Integer> pre;
private Queue<Integer> post;
private Stack<Integer> reversepost;
public DepthFirstOrder(Digraph G)
{
pre = new Queue<Integer>();
post = new Queue<Integer>();
reversepost = new Stack<Integer>();
marked = new boolean[G.V()];
for (int v = 0; v < G.V(); v++)
if (!marked[v]) dfs(G, v);
}
private void dfs(Digraph G, int v)
{
marked[v] = true;
pre.enqueue(v);
for (int w : G.adj(v))
if (!marked[w]) dfs(G, w);
post.enqueue(v);
reversepost.push(v);
}
public Iterable<Integer> pre()
{
return pre;
}
public Iterable<Integer> post()
{
return post;
}
public Iterable<Integer> reversepost()
{
return reversepost;
}
}
十一、拓扑排序:
public class Topological
{
private Iterable<Integer> order;
public Topological(Digraph G)
{
DirectedCycle cyclefinder = new DirectedCycle(G);
if (!cyclefinder.hasCycle(G))
{
DepthFirstOrder dfs = new DepthFirstOrder(G);
order = dfs.reversepost();
}
}
public Iterable<Integer> order()
{
return order;
}
public boolean isDAG()
{
return order != null;
}
}
十二、计算强连通分量的Kosaraju算法:
public class KosarajuSCC
{
private boolean marked[];
private int[] id;
private int count;
public KosarajuSCC(Digraph G)
{
id = new int[G.V()];
marked = new boolean[G.V()];
DepthFirstOrder order = new DepthFirstOrder(G.reverse());
for (int s : order.reversepost())
{
if (!marked[s])
{dfs(G, s); count++;}
}
}
private void dfs(Digraph G, int s)
{
marked[s] = true;
id[s] = count;
for (int v : G.adj(s))
{
if (!marked[v])
dfs(G, v);
}
}
public boolean Stronglyconnected(int v, int w)
{
return id[v] == id[w];
}
public int count()
{return count;}
public int id(int v)
{return id[v];}
}
十三、顶点对的可达性:
public class TransitiveClosure
{
private DirectedDFS[] all;
TransitiveClosure(Digraph G)
{
all = new DirectedDFS[G.V()];
for (int v = 0; v < G.V(); v++)
{
all[v] = new DirectedDFS(G, v);
}
}
boolean reachable(int v, int w)
{
return all[v].marked(w);
}
}
十四、加权无向图数据类型:
public class Edge implements Comparable<Edge>
{
private final int v;
private final int w;
private final double weight;
public Edge(int v, int w, double weight)
{
this.v = v;
this.w = w;
this.weight = weight;
}
public double weight()
{
return weight;
}
public int either()
{
return v;
}
public int other(int vertex)
{
if (vertex == v) return w;
else return v;
}
public int compareTo(Edge that)
{
if (that.weight() > this.weight()) return -1;
else if (that.weight() < this.weight()) return 1;
else return 0;
}
public String toString()
{
return String.format("%d-%d %.2f", v, w, weight);
}
}
public class EdgeWeightedGraph
{
private final int V;
private int E;
Bag<Edge>[] adj;
public EdgeWeightedGraph(int v)
{
this.V = v;
this.E = 0;
adj = (Bag<Edge>[])new Bag[V];
for (int i = 0; i < V; i++)
{
adj[i] = new Bag<Edge>();
}
}
public EdgeWeightedGraph(In in)
{
this(in.readInt());
int E = in.readInt();
if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative");
for (int i = 0; i < E; i++) {
int v = in.readInt();
int w = in.readInt();
double weight = in.readDouble();
Edge e = new Edge(v, w, weight);
addEdge(e);
}
}
public int V()
{
return V;
}
public int E()
{
return E;
}
public void addEdge(Edge e)
{
int v = e.either();
int w = e.other(v);
adj[v].add(e);
adj[w].add(e);
E++;
}
public Iterable<Edge> edges()
{
Bag<Edge> b = new Bag<Edge>();
for (int v = 0; v < V(); v++)
{
for (Edge e : adj[v])
{
if (e.other(v) > v) b.add(e);
}
}
return b;
}
}
十五、Prim最小生成树算法的延时实现:
public class LazyPrimMST
{
private boolean marked[];
private Queue<Edge> mst;
private MinPQ<Edge> pq;
private double weight;
public LazyPrimMST(EdgeWeightedGraph G)
{
weight = 0;
marked = new boolean[G.V()];
mst = new Queue<Edge>();
pq = new MinPQ<Edge>();
visit(G, 0);
while (!pq.isEmpty())
{
Edge e = pq.delMin();
int v = e.either();
int w = e.other(v);
if (marked[v] && marked[w]) continue;
mst.enqueue(e);
weight += e.weight();
if (!marked[v]) visit(G, v);
if (!marked[w]) visit(G, w);
}
}
private void visit(EdgeWeightedGraph G, int v)
{
marked[v] = true;
for (Edge e : G.adj[v])
if (!marked[e.other(v)])
pq.insert(e);
}
public Iterable<Edge> edges()
{return mst;}
public double weight()
{
return weight;
}
}
十六、Prim最小生成树的即时算法:
public class PrimMST
{
private boolean[] marked;
private Edge[] edgeTo;
private double[] distTo;
private IndexMinPQ<Edge> pq;
public PrimMST(EdgeWeightedGraph G)
{
marked = new boolean[G.V()];
edgeTo = new Edge[G.V()];
distTo = new double[G.V()];
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
pq = new IndexMinPQ<Edge>(G.V());
distTo[0] = 0.0;
pq.insert(0, 0.0);
while(!pq.isEmpty())
{
visit(G, pq.delMin());
}
}
private void visit(EdgeWeightedGraph G, int v)
{
marked[v] = true;
for (Edge e : G.adj[v])
{
int w = e.other(v);
if(marked[w]) continue;
if (e.weight() < distTo[w])
{
edgeTo[w] = e;
distTo[w] = e.weight();
if (pq.contains(w)) pq.change(w, distTo[w]);
else pq.insert(w, distTo[w]);
}
}
}
public Iterable<Edge> edges() {
Queue<Edge> mst = new Queue<Edge>();
for (int v = 0; v < edgeTo.length; v++) {
Edge e = edgeTo[v];
if (e != null) {
mst.enqueue(e);
}
}
return mst;
}
public double weight() {
double weight = 0.0;
for (Edge e : edges())
weight += e.weight();
return weight;
}
}
十七、Krustal最小生成树算法:
public class KrustalMST
{
private Queue<Edge> mst;
public KrustalMST(EdgeWeightedGraph G)
{
mst = new Queue<Edge>();
MinPQ<Edge> pq = new MinPQ<Edge>();
for (Edge e : G.edges()) pq.insert(e);
UF uf = new UF(G.V());
while (!pq.isEmpty() && mst.size() < G.V() - 1)
{
Edge e = pq.delMin();
int v = e.either();
int w = e.other(v);
if (uf.connected(v, w)) continue;
uf.union(v, w);
mst.enqueue(e);
}
}
public Iterable<Edge> edges()
{
return mst;
}
public double weight()
{
double weight = 0;
for (Edge e : edges())
{
weight += e.weight();
}
return weight;
}
}
十八、加权有向图的数据类型:
public class DirectedEdge
{
private int v;
private int w;
private double weight;
public DirectedEdge(int v, int w, double weight)
{
this.v = v;
this.w = w;
this.weight = weight;
}
public int from()
{
return v;
}
public int to()
{
return w;
}
public double weight()
{
return weight;
}
public String toString()
{
return String.format("%d-%d %.2f", v, w, weight);
}
}
public class EdgeWeightedDigraph
{
private final int V;
private int E;
Bag<DirectedEdge>[] adj;
public EdgeWeightedDigraph(int v)
{
this.V = v;
this.E = 0;
adj = (Bag<DirectedEdge>[])new Bag[V];
for (int i = 0; i < V; i++)
{
adj[i] = new Bag<DirectedEdge>();
}
}
public EdgeWeightedDigraph(In in)
{
this(in.readInt());
int E = in.readInt();
if (E < 0) throw new IllegalArgumentException("Number of edges must be nonnegative");
for (int i = 0; i < E; i++) {
int v = in.readInt();
int w = in.readInt();
double weight = in.readDouble();
DirectedEdge e = new DirectedEdge(v, w, weight);
addEdge(e);
}
}
public int V()
{
return V;
}
public int E()
{
return E;
}
public void addEdge(DirectedEdge e)
{
int v = e.from();
// int w = e.other(v);
adj[v].add(e);
// adj[w].add(e);
E++;
}
public Iterable<DirectedEdge> edges()
{
Bag<DirectedEdge> b = new Bag<DirectedEdge>();
for (int v = 0; v < V(); v++)
{
for (DirectedEdge e : adj[v])
{
b.add(e);
}
}
return b;
}
}
十九、Dijkstra最短路径算法(加权有向图/无向图 ,不支持负权重)
public class DijstraSP
{
private DirectedEdge[] edgeTo;
private double[] distTo;
private IndexMinPQ<Double> pq;
public DijstraSP(EdgeWeightedDigraph G, int s)
{
edgeTo = new DirectedEdge[G.V()];
distTo = new double[G.V()];
pq = new IndexMinPQ<Double>(G.V());
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
distTo[s] = 0.0;
pq.insert(s, 0.0);
while (!pq.isEmpty())
{
relax(G, pq.delMin());
}
}
private void relax(EdgeWeightedDigraph G, int v)
{
for (DirectedEdge e : G.adj[v])
{
int w = e.to();
if (distTo[w] > distTo[v] + e.weight())
{
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
if (pq.contains(w)) pq.change(w, distTo[w]);
else pq.insert(w, distTo[w]);
}
}
}
public double distTo(int v)
{
return distTo[v];
}
public boolean hasPathTo(int v)
{
return distTo[v] != Double.POSITIVE_INFINITY;
}
public Iterable<DirectedEdge> pathTo(int v)
{
if (!hasPathTo(v)) return null;
Stack<DirectedEdge> path = new Stack<DirectedEdge>();
for (DirectedEdge e = edgeTo[v] ; e != null; e = edgeTo[e.from()])
path.push(e);
return path;
}
}
二十、加权有向无环图的最短路径算法:
public class AcyclicSP
{
private DirectedEdge[] edgeTo;
private double[] distTo;
public AcycliSP(EdgeWeightedDigraph G, int s)
{
edgeTo = new DirectedEdge[G.V()];
distTo = new double[G.V()];
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
distTo[s] = 0.0;
Topological top = new Topological(G);
for (int v : top.order())
relax(G, v);
}
private void relax(EdgeWeightedDigraph G, int v)
{
for (DirectedEdge e : G.adj[v])
{
int w = e.to();
if (distTo[w] > distTo[v] + e.weight())
{
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
}
}
}
public double distTo(int v)
{
return distTo[v];
}
public boolean hasPathTo(int v)
{
return distTo[v] != Double.POSITIVE_INFINITY;
}
public Iterable<DirectedEdge> pathTo(int v)
{
if (!hasPathTo(v)) return null;
Stack<DirectedEdge> path = new Stack<DirectedEdge>();
for (DirectedEdge e = edgeTo[v] ; e != null; e = edgeTo[e.from()])
path.push(e);
return path;
}
}
二十一、一般加权有向图(负权重、环)的最短路径问题BellmanFord算法:
public class BellFordmanSP
{
private DirectedEdge[] edgeTo;
private double[] distTo;
private int cost;
private Queue<Integer> queue;
private boolean[] onQ;
private Iterable<DirectedEdge> cycle;
public BellFordmanSP(EdgeWeightedDigraph G, int s)
{
edgeTo = new DirectedEdge[G.V()];
distTo = new double[G.V()];
onQ = new boolean[G.V()];
queue = new Queue<Integer>();
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
distTo[s] = 0.0;
queue.enqueue(s);
onQ[s] = true;
while (!queue.isEmpty() && !hasNegativeCycle())
{
int v = queue.dequeue();
onQ[v] = false;
relax(G, v);
}
}
private void relax(EdgeWeightedDigraph G, int v)
{
for (DirectedEdge e : G.adj[v])
{
int w = e.to();
if (distTo[w] > distTo[v] + e.weight())
{
distTo[w] = distTo[v] + e.weight();
edgeTo[w] = e;
if (!onQ[w]) queue.enqueue(w);
onQ[w] = true;
}
}
if (cost++ % G.V() == 0)
findNegativeCycle();
}
private void findNegativeCycle()
{
int V = edgeTo.length;
EdgeWeightedDigraph spt;
spt = new EdgeWeightedDigraph(V);
for (int v = 0; v < V; v++)
if (edgeTo[v] != null)
spt.addEdge(edgeTo[v]);
EdgeWeightedCycleFinder cf = new EdgeWeightedCycleFinder(spt);
cycle = cf.cycle();
}
}