加权无向图
加权图是为每条边关联一个权值的图模型,这种图可以自然的表示很多应用,在一副航班图中边表示航线,权值代表距离或价格,电路图中边表示电线,权值代表电线长度
边的权重不一定是距离,也可能是时间,费用,也可能是0或负数
加权无向图代码实现如下,只用把Graph实现里的Bag<Integer>换成Bag<Edge>,用Edge表示有权重的边
public class EdgeWeightedGraph {
private static final String NEWLINE = System.getProperty(“line.separator”);
private final int V;
private int E;
private Bag<Edge>[] adj;
/**
* Initializes an empty edge-weighted graph with {@code V} vertices and 0 edges.
*
* @param V the number of vertices
* @throws IllegalArgumentException if {@code V < 0}
*/
public EdgeWeightedGraph(int V) {
if (V < 0) throw new IllegalArgumentException(“Number of vertices must be nonnegative”);
this.V = V;
this.E = 0;
adj = (Bag<Edge>[]) new Bag[V];
for (int v = 0; v < V; v++) {
adj[v] = new Bag<Edge>();
}
}
/**
* Initializes a random edge-weighted graph with {@code V} vertices and <em>E</em> edges.
*
* @param V the number of vertices
* @param E the number of edges
* @throws IllegalArgumentException if {@code V < 0}
* @throws IllegalArgumentException if {@code E < 0}
*/
public EdgeWeightedGraph(int V, int E) {
this(V);
if (E < 0) throw new IllegalArgumentException(“Number of edges must be nonnegative”);
for (int i = 0; i < E; i++) {
int v = StdRandom.uniform(V);
int w = StdRandom.uniform(V);
double weight = Math.round(100 * StdRandom.uniform()) / 100.0;
Edge e = new Edge(v, w, weight);
addEdge(e);
}
}
/**
* Initializes an edge-weighted graph from an input stream.
* The format is the number of vertices <em>V</em>,
* followed by the number of edges <em>E</em>,
* followed by <em>E</em> pairs of vertices and edge weights,
* with each entry separated by whitespace.
*
* @param in the input stream
* @throws IllegalArgumentException if the endpoints of any edge are not in prescribed range
* @throws IllegalArgumentException if the number of vertices or edges is negative
*/
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();
validateVertex(v);
validateVertex(w);
double weight = in.readDouble();
Edge e = new Edge(v, w, weight);
addEdge(e);
}
}
/**
* Initializes a new edge-weighted graph that is a deep copy of {@code G}.
*
* @param G the edge-weighted graph to copy
*/
public EdgeWeightedGraph(EdgeWeightedGraph G) {
this(G.V());
this.E = G.E();
for (int v = 0; v < G.V(); v++) {
// reverse so that adjacency list is in same order as original
Stack<Edge> reverse = new Stack<Edge>();
for (Edge e : G.adj[v]) {
reverse.push(e);
}
for (Edge e : reverse) {
adj[v].add(e);
}
}
}
/**
* Returns the number of vertices in this edge-weighted graph.
*
* @return the number of vertices in this edge-weighted graph
*/
public int V() {
return V;
}
/**
* Returns the number of edges in this edge-weighted graph.
*
* @return the number of edges in this edge-weighted graph
*/
public int E() {
return E;
}
// throw an IllegalArgumentException unless {@code 0 <= v < V}
private void validateVertex(int v) {
if (v < 0 || v >= V)
throw new IllegalArgumentException(“vertex ” + v + ” is not between 0 and ” + (V-1));
}
/**
* Adds the undirected edge {@code e} to this edge-weighted graph.
*
* @param e the edge
* @throws IllegalArgumentException unless both endpoints are between {@code 0} and {@code V-1}
*/
public void addEdge(Edge e) {
int v = e.either();
int w = e.other(v);
validateVertex(v);
validateVertex(w);
adj[v].add(e);
adj[w].add(e);
E++;
}
/**
* Returns the edges incident on vertex {@code v}.
*
* @param v the vertex
* @return the edges incident on vertex {@code v} as an Iterable
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public Iterable<Edge> adj(int v) {
validateVertex(v);
return adj[v];
}
/**
* Returns the degree of vertex {@code v}.
*
* @param v the vertex
* @return the degree of vertex {@code v}
* @throws IllegalArgumentException unless {@code 0 <= v < V}
*/
public int degree(int v) {
validateVertex(v);
return adj[v].size();
}
/**
* Returns all edges in this edge-weighted graph.
* To iterate over the edges in this edge-weighted graph, use foreach notation:
* {@code for (Edge e : G.edges())}.
*
* @return all edges in this edge-weighted graph, as an iterable
*/
public Iterable<Edge> edges() {
Bag<Edge> list = new Bag<Edge>();
for (int v = 0; v < V; v++) {
int selfLoops = 0;
for (Edge e : adj(v)) {
if (e.other(v) > v) {
list.add(e);
}
// add only one copy of each self loop (self loops will be consecutive)
else if (e.other(v) == v) {
if (selfLoops % 2 == 0) list.add(e);
selfLoops++;
}
}
}
return list;
}
/**
* Returns a string representation of the edge-weighted graph.
* This method takes time proportional to <em>E</em> + <em>V</em>.
*
* @return the number of vertices <em>V</em>, followed by the number of edges <em>E</em>,
* followed by the <em>V</em> adjacency lists of edges
*/
public String toString() {
StringBuilder s = new StringBuilder();
s.append(V + ” ” + E + NEWLINE);
for (int v = 0; v < V; v++) {
s.append(v + “: “);
for (Edge e : adj[v]) {
s.append(e + ” “);
}
s.append(NEWLINE);
}
return s.toString();
}
/**
* Unit tests the {@code EdgeWeightedGraph} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedGraph G = new EdgeWeightedGraph(in);
StdOut.println(G);
}
}
/******************************************************************************
* Compilation: javac Edge.java
* Execution: java Edge
* Dependencies: StdOut.java
*
* Immutable weighted edge.
*
******************************************************************************/
/**
* The {@code Edge} class represents a weighted edge in an
* {@link EdgeWeightedGraph}. Each edge consists of two integers
* (naming the two vertices) and a real-value weight. The data type
* provides methods for accessing the two endpoints of the edge and
* the weight. The natural order for this data type is by
* ascending order of weight.
* <p>
* For additional documentation, see <a href=”https://algs4.cs.princeton.edu/43mst”>Section 4.3</a> of
* <i>Algorithms, 4th Edition</i> by Robert Sedgewick and Kevin Wayne.
*
* @author Robert Sedgewick
* @author Kevin Wayne
*/
public class Edge implements Comparable<Edge> {
private final int v;
private final int w;
private final double weight;
/**
* Initializes an edge between vertices {@code v} and {@code w} of
* the given {@code weight}.
*
* @param v one vertex
* @param w the other vertex
* @param weight the weight of this edge
* @throws IllegalArgumentException if either {@code v} or {@code w}
* is a negative integer
* @throws IllegalArgumentException if {@code weight} is {@code NaN}
*/
public Edge(int v, int w, double weight) {
if (v < 0) throw new IllegalArgumentException(“vertex index must be a nonnegative integer”);
if (w < 0) throw new IllegalArgumentException(“vertex index must be a nonnegative integer”);
if (Double.isNaN(weight)) throw new IllegalArgumentException(“Weight is NaN”);
this.v = v;
this.w = w;
this.weight = weight;
}
/**
* Returns the weight of this edge.
*
* @return the weight of this edge
*/
public double weight() {
return weight;
}
/**
* Returns either endpoint of this edge.
*
* @return either endpoint of this edge
*/
public int either() {
return v;
}
/**
* Returns the endpoint of this edge that is different from the given vertex.
*
* @param vertex one endpoint of this edge
* @return the other endpoint of this edge
* @throws IllegalArgumentException if the vertex is not one of the
* endpoints of this edge
*/
public int other(int vertex) {
if (vertex == v) return w;
else if (vertex == w) return v;
else throw new IllegalArgumentException(“Illegal endpoint”);
}
/**
* Compares two edges by weight.
* Note that {@code compareTo()} is not consistent with {@code equals()},
* which uses the reference equality implementation inherited from {@code Object}.
*
* @param that the other edge
* @return a negative integer, zero, or positive integer depending on whether
* the weight of this is less than, equal to, or greater than the
* argument edge
*/
@Override
public int compareTo(Edge that) {
return Double.compare(this.weight, that.weight);
}
/**
* Returns a string representation of this edge.
*
* @return a string representation of this edge
*/
public String toString() {
return String.format(“%d-%d %.5f”, v, w, weight);
}
/**
* Unit tests the {@code Edge} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
Edge e = new Edge(12, 34, 5.67);
StdOut.println(e);
}
}
/******************************************************************************
* Compilation: javac LazyPrimMST.java
* Execution: java LazyPrimMST filename.txt
* Dependencies: EdgeWeightedGraph.java Edge.java Queue.java
* MinPQ.java UF.java In.java StdOut.java
* Data files: https://algs4.cs.princeton.edu/43mst/tinyEWG.txt
* https://algs4.cs.princeton.edu/43mst/mediumEWG.txt
* https://algs4.cs.princeton.edu/43mst/largeEWG.txt
*
* Compute a minimum spanning forest using a lazy version of Prim’s
* algorithm.
*
* % java LazyPrimMST tinyEWG.txt
* 0-7 0.16000
* 1-7 0.19000
* 0-2 0.26000
* 2-3 0.17000
* 5-7 0.28000
* 4-5 0.35000
* 6-2 0.40000
* 1.81000
*
* % java LazyPrimMST mediumEWG.txt
* 0-225 0.02383
* 49-225 0.03314
* 44-49 0.02107
* 44-204 0.01774
* 49-97 0.03121
* 202-204 0.04207
* 176-202 0.04299
* 176-191 0.02089
* 68-176 0.04396
* 58-68 0.04795
* 10.46351
*
* % java LazyPrimMST largeEWG.txt
* …
* 647.66307
*
******************************************************************************/
/**
* The {@code LazyPrimMST} class represents a data type for computing a
* <em>minimum spanning tree</em> in an edge-weighted graph.
* The edge weights can be positive, zero, or negative and need not
* be distinct. If the graph is not connected, it computes a <em>minimum
* spanning forest</em>, which is the union of minimum spanning trees
* in each connected component. The {@code weight()} method returns the
* weight of a minimum spanning tree and the {@code edges()} method
* returns its edges.
* <p>
* This implementation uses a lazy version of <em>Prim’s algorithm</em>
* with a binary heap of edges.
* The constructor takes time proportional to <em>E</em> log <em>E</em>
* and extra space (not including the graph) proportional to <em>E</em>,
* where <em>V</em> is the number of vertices and <em>E</em> is the number of edges.
* Afterwards, the {@code weight()} method takes constant time
* and the {@code edges()} method takes time proportional to <em>V</em>.
* <p>
* For additional documentation,
* see <a href=”https://algs4.cs.princeton.edu/43mst”>Section 4.3</a> of
* <i>Algorithms, 4th Edition</i> by Robert Sedgewick and Kevin Wayne.
* For alternate implementations, see {@link PrimMST}, {@link KruskalMST},
* and {@link BoruvkaMST}.
*
* @author Robert Sedgewick
* @author Kevin Wayne
*/
最小生成树
在加权无向图的应用中最让人感兴趣的是怎样让成本最小化,就是要找到图的一副子图,连接图中所有顶点并且使所有边的权值总和最小,这种子图叫最小生成树,一副图的生成树是连接图中所有顶点的的无环连通子图,最小生成树就是所有边的权值总和最小的那颗。讨论最小生成树我们假设图是连通图,并且每条边的权重不同,
我们可以用下面的思路来计算无向图的最小生成树,把图的顶点分成两个部分,一部分顶点属于某颗生成树,一部分不属于,
先把一个顶点加入生成树,在从剩下的顶点中找一个离树最近的顶点及边加入,再从剩下的顶点中找一个离树最近的顶点及边加入
反复操作直到所有顶点都进入了这颗生成树,因为所有顶点离树的距离都是最近的,所以这个树就是最小生成树。
这种算法被称为prim算法,prim算法分延时实现和及时实现两个版本,先看延时实现,结合下图来理解
先把0加入生成树,并把0的所有边存入最小堆pq中,pq用来存放所有可以到达生成树的边,从pq中找出最小的边0-7加入生成树,并把顶点7的所有领边加入pq
因为0和7都属于生成树,从0和7的领边都可以到达生成树,再从pq中找出最小的边7-1加入生成树,并把顶点1的所有领边加入pq,再从pq中找出最小的边0-2加入生成树,并把顶点2的所有领边加入pq,反复直到pq为空所有顶点加入最小生成树,如果pq中一条边的两个顶点都已加入生成树,则废弃这条边
//prim算法的延时实现代码如下
public class LazyPrimMST {
private static final double FLOATING_POINT_EPSILON = 1E-12;
private double weight; // total weight of MST
private Queue<Edge> mst; // edges in the MST
private boolean[] marked; // marked[v] = true iff v on tree
private MinPQ<Edge> pq; // edges with one endpoint in tree
/**
* Compute a minimum spanning tree (or forest) of an edge-weighted graph.
* @param G the edge-weighted graph
*/
public LazyPrimMST(EdgeWeightedGraph G) {
mst = new Queue<Edge>();
pq = new MinPQ<Edge>();
marked = new boolean[G.V()];
for (int v = 0; v < G.V(); v++) // run Prim from all vertices to
if (!marked[v]) prim(G, v); // get a minimum spanning forest
// check optimality conditions
assert check(G);
}
// run Prim’s algorithm
private void prim(EdgeWeightedGraph G, int s) {
scan(G, s);
while (!pq.isEmpty()) { // better to stop when mst has V-1 edges
Edge e = pq.delMin(); // smallest edge on pq
int v = e.either(), w = e.other(v); // two endpoints
assert marked[v] || marked[w];
if (marked[v] && marked[w]) continue; // lazy, both v and w already scanned
mst.enqueue(e); // add e to MST
weight += e.weight();
if (!marked[v]) scan(G, v); // v becomes part of tree
if (!marked[w]) scan(G, w); // w becomes part of tree
}
}
// add all edges e incident to v onto pq if the other endpoint has not yet been scanned
private void scan(EdgeWeightedGraph G, int v) {
assert !marked[v];
marked[v] = true;
for (Edge e : G.adj(v))
if (!marked[e.other(v)]) pq.insert(e);
}
/**
* Returns the edges in a minimum spanning tree (or forest).
* @return the edges in a minimum spanning tree (or forest) as
* an iterable of edges
*/
public Iterable<Edge> edges() {
return mst;
}
/**
* Returns the sum of the edge weights in a minimum spanning tree (or forest).
* @return the sum of the edge weights in a minimum spanning tree (or forest)
*/
public double weight() {
return weight;
}
// check optimality conditions (takes time proportional to E V lg* V)
private boolean check(EdgeWeightedGraph G) {
// check weight
double totalWeight = 0.0;
for (Edge e : edges()) {
totalWeight += e.weight();
}
if (Math.abs(totalWeight – weight()) > FLOATING_POINT_EPSILON) {
System.err.printf(“Weight of edges does not equal weight(): %f vs. %f\n”, totalWeight, weight());
return false;
}
// check that it is acyclic
UF uf = new UF(G.V());
for (Edge e : edges()) {
int v = e.either(), w = e.other(v);
if (uf.connected(v, w)) {
System.err.println(“Not a forest”);
return false;
}
uf.union(v, w);
}
// check that it is a spanning forest
for (Edge e : G.edges()) {
int v = e.either(), w = e.other(v);
if (!uf.connected(v, w)) {
System.err.println(“Not a spanning forest”);
return false;
}
}
// check that it is a minimal spanning forest (cut optimality conditions)
for (Edge e : edges()) {
// all edges in MST except e
uf = new UF(G.V());
for (Edge f : mst) {
int x = f.either(), y = f.other(x);
if (f != e) uf.union(x, y);
}
// check that e is min weight edge in crossing cut
for (Edge f : G.edges()) {
int x = f.either(), y = f.other(x);
if (!uf.connected(x, y)) {
if (f.weight() < e.weight()) {
System.err.println(“Edge ” + f + ” violates cut optimality conditions”);
return false;
}
}
}
}
return true;
}
/**
* Unit tests the {@code LazyPrimMST} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedGraph G = new EdgeWeightedGraph(in);
LazyPrimMST mst = new LazyPrimMST(G);
for (Edge e : mst.edges()) {
StdOut.println(e);
}
StdOut.printf(“%.5f\n”, mst.weight());
}
}
prim算法的延时实现中会把一个顶点可以到达生成树的所有边都加入pq,然后遍历pq发现这条边不是最小边再废弃
而prim算法的即时实现是只把一个顶点的一条可到达生成树的边加入pq,扫描的过程中发现这条边不是最小的边再把它替换成更小的边
//轨迹图如下
//代码实现如下
public class PrimMST {
private static final double FLOATING_POINT_EPSILON = 1E-12;
private Edge[] edgeTo; // 用来存放每个顶点到达生成树的最小边
private double[] distTo; // 用来存放每个顶点到达生成树的最短距离
private boolean[] marked; // marked[v] = true if v on tree, false otherwise
private IndexMinPQ<Double> pq;
/**
* Compute a minimum spanning tree (or forest) of an edge-weighted graph.
* @param G the edge-weighted graph
*/
public PrimMST(EdgeWeightedGraph G) {
edgeTo = new Edge[G.V()];
distTo = new double[G.V()];
marked = new boolean[G.V()];
pq = new IndexMinPQ<Double>(G.V());
for (int v = 0; v < G.V(); v++)
distTo[v] = Double.POSITIVE_INFINITY;
for (int v = 0; v < G.V(); v++) // run from each vertex to find
if (!marked[v]) prim(G, v); // minimum spanning forest
// check optimality conditions
assert check(G);
}
// run Prim’s algorithm in graph G, starting from vertex s
private void prim(EdgeWeightedGraph G, int s) {
distTo[s] = 0.0;
pq.insert(s, distTo[s]);
while (!pq.isEmpty()) {
int v = pq.delMin();
scan(G, v);
}
}
// scan vertex v
private void scan(EdgeWeightedGraph G, int v) {
marked[v] = true;
for (Edge e : G.adj(v)) {
int w = e.other(v);
if (marked[w]) continue; // v-w is obsolete edge
if (e.weight() < distTo[w]) { //找到w到生成树更小的边,更小pq中w的信息
distTo[w] = e.weight();
edgeTo[w] = e;
if (pq.contains(w)) pq.decreaseKey(w, distTo[w]);
else pq.insert(w, distTo[w]);
}
}
}
/**
* Returns the edges in a minimum spanning tree (or forest).
* @return the edges in a minimum spanning tree (or forest) as
* an iterable of edges
*/
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;
}
/**
* Returns the sum of the edge weights in a minimum spanning tree (or forest).
* @return the sum of the edge weights in a minimum spanning tree (or forest)
*/
public double weight() {
double weight = 0.0;
for (Edge e : edges())
weight += e.weight();
return weight;
}
// check optimality conditions (takes time proportional to E V lg* V)
private boolean check(EdgeWeightedGraph G) {
// check weight
double totalWeight = 0.0;
for (Edge e : edges()) {
totalWeight += e.weight();
}
if (Math.abs(totalWeight – weight()) > FLOATING_POINT_EPSILON) {
System.err.printf(“Weight of edges does not equal weight(): %f vs. %f\n”, totalWeight, weight());
return false;
}
// check that it is acyclic
UF uf = new UF(G.V());
for (Edge e : edges()) {
int v = e.either(), w = e.other(v);
if (uf.connected(v, w)) {
System.err.println(“Not a forest”);
return false;
}
uf.union(v, w);
}
// check that it is a spanning forest
for (Edge e : G.edges()) {
int v = e.either(), w = e.other(v);
if (!uf.connected(v, w)) {
System.err.println(“Not a spanning forest”);
return false;
}
}
// check that it is a minimal spanning forest (cut optimality conditions)
for (Edge e : edges()) {
// all edges in MST except e
uf = new UF(G.V());
for (Edge f : edges()) {
int x = f.either(), y = f.other(x);
if (f != e) uf.union(x, y);
}
// check that e is min weight edge in crossing cut
for (Edge f : G.edges()) {
int x = f.either(), y = f.other(x);
if (!uf.connected(x, y)) {
if (f.weight() < e.weight()) {
System.err.println(“Edge ” + f + ” violates cut optimality conditions”);
return false;
}
}
}
}
return true;
}
/**
* Unit tests the {@code PrimMST} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedGraph G = new EdgeWeightedGraph(in);
PrimMST mst = new PrimMST(G);
for (Edge e : mst.edges()) {
StdOut.println(e);
}
StdOut.printf(“%.5f\n”, mst.weight());
}
}
prim算法计算过程中就一颗生成树,每一步为生成树增加一条边,直到所有顶点都加入生成树
而kruskal算法的计算机过程中有多个生成树构成森林,首先把所有边按权值大小加入优先队列pq,每一步从pq中取出权值最小的边加入森林,如果这条边加入森林后形成了环则不加入这条边,计算过程中两个生成树会逐渐合并成一棵生成树,直到最后只剩下一个生成树。kruskal算法的计算轨迹如下图,计算过程中逐渐把0-7,2-3,1-7,0-2,5-7加入森林,当加入1-3时会构成环,所以丢弃边1-3,直到pq中所有边遍历完成
//代码实现如下
public class KruskalMST {
private static final double FLOATING_POINT_EPSILON = 1E-12;
private double weight; // weight of MST
private Queue<Edge> mst = new Queue<Edge>(); // edges in MST
/**
* Compute a minimum spanning tree (or forest) of an edge-weighted graph.
* @param G the edge-weighted graph
*/
public KruskalMST(EdgeWeightedGraph G) {
// more efficient to build heap by passing array of edges
MinPQ<Edge> pq = new MinPQ<Edge>();
for (Edge e : G.edges()) {
pq.insert(e);
}
// run greedy algorithm
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)) { // v-w does not create a cycle ,判断v-w这条边加入后是否会形成环
uf.union(v, w); // merge v and w components,将v-w加入森林
mst.enqueue(e); // add edge e to mst
weight += e.weight();
}
}
// check optimality conditions
assert check(G);
}
/**
* Returns the edges in a minimum spanning tree (or forest).
* @return the edges in a minimum spanning tree (or forest) as
* an iterable of edges
*/
public Iterable<Edge> edges() {
return mst;
}
/**
* Returns the sum of the edge weights in a minimum spanning tree (or forest).
* @return the sum of the edge weights in a minimum spanning tree (or forest)
*/
public double weight() {
return weight;
}
// check optimality conditions (takes time proportional to E V lg* V)
private boolean check(EdgeWeightedGraph G) {
// check total weight
double total = 0.0;
for (Edge e : edges()) {
total += e.weight();
}
if (Math.abs(total – weight()) > FLOATING_POINT_EPSILON) {
System.err.printf(“Weight of edges does not equal weight(): %f vs. %f\n”, total, weight());
return false;
}
// check that it is acyclic
UF uf = new UF(G.V());
for (Edge e : edges()) {
int v = e.either(), w = e.other(v);
if (uf.connected(v, w)) {
System.err.println(“Not a forest”);
return false;
}
uf.union(v, w);
}
// check that it is a spanning forest
for (Edge e : G.edges()) {
int v = e.either(), w = e.other(v);
if (!uf.connected(v, w)) {
System.err.println(“Not a spanning forest”);
return false;
}
}
// check that it is a minimal spanning forest (cut optimality conditions)
for (Edge e : edges()) {
// all edges in MST except e
uf = new UF(G.V());
for (Edge f : mst) {
int x = f.either(), y = f.other(x);
if (f != e) uf.union(x, y);
}
// check that e is min weight edge in crossing cut
for (Edge f : G.edges()) {
int x = f.either(), y = f.other(x);
if (!uf.connected(x, y)) {
if (f.weight() < e.weight()) {
System.err.println(“Edge ” + f + ” violates cut optimality conditions”);
return false;
}
}
}
}
return true;
}
/**
* Unit tests the {@code KruskalMST} data type.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
In in = new In(args[0]);
EdgeWeightedGraph G = new EdgeWeightedGraph(in);
KruskalMST mst = new KruskalMST(G);
for (Edge e : mst.edges()) {
StdOut.println(e);
}
StdOut.printf(“%.5f\n”, mst.weight());
}
}
/******************************************************************************
* Compilation: javac UF.java
* Execution: java UF < input.txt
* Dependencies: StdIn.java StdOut.java
* Data files: https://algs4.cs.princeton.edu/15uf/tinyUF.txt
* https://algs4.cs.princeton.edu/15uf/mediumUF.txt
* https://algs4.cs.princeton.edu/15uf/largeUF.txt
*
* Weighted quick-union by rank with path compression by halving.
*
* % java UF < tinyUF.txt
* 4 3
* 3 8
* 6 5
* 9 4
* 2 1
* 5 0
* 7 2
* 6 1
* 2 components
*
******************************************************************************/
/**
* The {@code UF} class represents a <em>union–find data type</em>
* (also known as the <em>disjoint-sets data type</em>).
* It supports the <em>union</em> and <em>find</em> operations,
* along with a <em>connected</em> operation for determining whether
* two sites are in the same component and a <em>count</em> operation that
* returns the total number of components.
* <p>
* The union–find data type models connectivity among a set of <em>n</em>
* sites, named 0 through <em>n</em>–1.
* The <em>is-connected-to</em> relation must be an
* <em>equivalence relation</em>:
* <ul>
* <li> <em>Reflexive</em>: <em>p</em> is connected to <em>p</em>.
* <li> <em>Symmetric</em>: If <em>p</em> is connected to <em>q</em>,
* then <em>q</em> is connected to <em>p</em>.
* <li> <em>Transitive</em>: If <em>p</em> is connected to <em>q</em>
* and <em>q</em> is connected to <em>r</em>, then
* <em>p</em> is connected to <em>r</em>.
* </ul>
* <p>
* An equivalence relation partitions the sites into
* <em>equivalence classes</em> (or <em>components</em>). In this case,
* two sites are in the same component if and only if they are connected.
* Both sites and components are identified with integers between 0 and
* <em>n</em>–1.
* Initially, there are <em>n</em> components, with each site in its
* own component. The <em>component identifier</em> of a component
* (also known as the <em>root</em>, <em>canonical element</em>, <em>leader</em>,
* or <em>set representative</em>) is one of the sites in the component:
* two sites have the same component identifier if and only if they are
* in the same component.
* <ul>
* <li><em>union</em>(<em>p</em>, <em>q</em>) adds a
* connection between the two sites <em>p</em> and <em>q</em>.
* If <em>p</em> and <em>q</em> are in different components,
* then it replaces
* these two components with a new component that is the union of
* the two.
* <li><em>find</em>(<em>p</em>) returns the component
* identifier of the component containing <em>p</em>.
* <li><em>connected</em>(<em>p</em>, <em>q</em>)
* returns true if both <em>p</em> and <em>q</em>
* are in the same component, and false otherwise.
* <li><em>count</em>() returns the number of components.
* </ul>
* <p>
* The component identifier of a component can change
* only when the component itself changes during a call to
* <em>union</em>—it cannot change during a call
* to <em>find</em>, <em>connected</em>, or <em>count</em>.
* <p>
* This implementation uses weighted quick union by rank with path compression
* by halving.
* Initializing a data structure with <em>n</em> sites takes linear time.
* Afterwards, the <em>union</em>, <em>find</em>, and <em>connected</em>
* operations take logarithmic time (in the worst case) and the
* <em>count</em> operation takes constant time.
* Moreover, the amortized time per <em>union</em>, <em>find</em>,
* and <em>connected</em> operation has inverse Ackermann complexity.
* For alternate implementations of the same API, see
* {@link QuickUnionUF}, {@link QuickFindUF}, and {@link WeightedQuickUnionUF}.
*
* <p>
* For additional documentation, see <a href=”https://algs4.cs.princeton.edu/15uf”>Section 1.5</a> of
* <i>Algorithms, 4th Edition</i> by Robert Sedgewick and Kevin Wayne.
*
* @author Robert Sedgewick
* @author Kevin Wayne
*/
public class UF {
private int[] parent; // parent[i] = parent of i
private byte[] rank; // rank[i] = rank of subtree rooted at i (never more than 31)
private int count; // number of components
/**
* Initializes an empty union–find data structure with {@code n} sites
* {@code 0} through {@code n-1}. Each site is initially in its own
* component.
*
* @param n the number of sites
* @throws IllegalArgumentException if {@code n < 0}
*/
public UF(int n) {
if (n < 0) throw new IllegalArgumentException();
count = n;
parent = new int[n];
rank = new byte[n];
for (int i = 0; i < n; i++) {
parent[i] = i;
rank[i] = 0;
}
}
/**
* Returns the component identifier for the component containing site {@code p}.
*
* @param p the integer representing one site
* @return the component identifier for the component containing site {@code p}
* @throws IllegalArgumentException unless {@code 0 <= p < n}
*/
public int find(int p) {
validate(p);
while (p != parent[p]) {
parent[p] = parent[parent[p]]; // path compression by halving
p = parent[p];
}
return p;
}
/**
* Returns the number of components.
*
* @return the number of components (between {@code 1} and {@code n})
*/
public int count() {
return count;
}
/**
* Returns true if the the two sites are in the same component.
*
* @param p the integer representing one site
* @param q the integer representing the other site
* @return {@code true} if the two sites {@code p} and {@code q} are in the same component;
* {@code false} otherwise
* @throws IllegalArgumentException unless
* both {@code 0 <= p < n} and {@code 0 <= q < n}
*/
public boolean connected(int p, int q) {
return find(p) == find(q);
}
/**
* Merges the component containing site {@code p} with the
* the component containing site {@code q}.
*
* @param p the integer representing one site
* @param q the integer representing the other site
* @throws IllegalArgumentException unless
* both {@code 0 <= p < n} and {@code 0 <= q < n}
*/
public void union(int p, int q) {
int rootP = find(p);
int rootQ = find(q);
if (rootP == rootQ) return;
// make root of smaller rank point to root of larger rank
if (rank[rootP] < rank[rootQ]) parent[rootP] = rootQ;
else if (rank[rootP] > rank[rootQ]) parent[rootQ] = rootP;
else {
parent[rootQ] = rootP;
rank[rootP]++;
}
count–;
}
// validate that p is a valid index
private void validate(int p) {
int n = parent.length;
if (p < 0 || p >= n) {
throw new IllegalArgumentException(“index ” + p + ” is not between 0 and ” + (n-1));
}
}
/**
* Reads in a an integer {@code n} and a sequence of pairs of integers
* (between {@code 0} and {@code n-1}) from standard input, where each integer
* in the pair represents some site;
* if the sites are in different components, merge the two components
* and print the pair to standard output.
*
* @param args the command-line arguments
*/
public static void main(String[] args) {
int n = StdIn.readInt();
UF uf = new UF(n);
while (!StdIn.isEmpty()) {
int p = StdIn.readInt();
int q = StdIn.readInt();
if (uf.connected(p, q)) continue;
uf.union(p, q);
StdOut.println(p + ” ” + q);
}
StdOut.println(uf.count() + ” components”);
}
}