每日一省之————加权无向图的最小生成树算法(Prim/Kruskal算法)

1.带权重的边的数据结构


/**
 * 该类对象可以表示图中的一条边
 * @author lhever 2017年2月19日 下午5:10:49
 * @version v1.0
 */
public class Edge implements Comparable<Edge>
{
    private int v;
    private int w;
    private final double weight;

    /**
     * 构造
     * 
     * @param v
     * @param w
     * @param weight
     * @author lhever 2017年2月19日 下午5:14:30
     * @since v1.0
     */
    public Edge(int v, int w, double weight)
    {
        if (v < 0)
        {
            throw new IllegalArgumentException("顶点v的值必须是一个非负整数");
        }
        if (w < 0)
        {
            throw new IllegalArgumentException("顶点w的值必须是一个非负整数");
        }
        if (Double.isNaN(weight))
        {
            throw new IllegalArgumentException("权重不能是 NaN");
        }
        this.v = v;
        this.w = w;
        this.weight = weight;
    }

    /**
     * 返回权重
     * 
     * @return
     * @author lhever 2017年2月19日 下午5:15:41
     * @since v1.0
     */
    public double weight()
    {
        return weight;
    }

    /**
     * 返回边的其中一个顶点v
     * @return
     * @author lhever 2017年2月19日 下午5:15:54
     * @since v1.0
     */
    public int either()
    {
        return v;
    }

    /**
     * 返回构成一条边的除vertex的另外一个顶点
     * @param vertex
     * @return
     * @author lhever 2017年2月19日 下午5:16:16
     * @since v1.0
     */
    public int other(int vertex)
    {
        if (vertex == v)
        {
            return w;
        } else if (vertex == w)
        {
            return v;
        } else
        {
            throw new IllegalArgumentException("不合法的顶点");
        }
    }

    @Override
    public int compareTo(Edge other)
    {
        if (this.weight() < other.weight())
        {
            return -1;
        } else if (this.weight() > other.weight())
        {
            return 1;
        } else
        {
            return 0;
        }
    }

    public String toString()
    {
        return String.format("%d-%d %.5f", v, w, weight);
    }

    public static void main(String[] args)
    {
        Edge e = new Edge(4, 5, 78.98);
        System.out.println(e);
    }

}

2 加权无向图的数据结构


import java.util.ArrayList;
import java.util.List;

public class EdgeWeightedGraph
{
    private static final String NEWLINE = System.getProperty("line.separator");
    private final int V;
    private int E;
    private List<Edge>[] adj;

    @SuppressWarnings("unchecked")
    public EdgeWeightedGraph(int V)
    {
        if (V < 0)
        {
            throw new IllegalArgumentException("顶点数必须是非负数");
        }
        this.V = V;
        this.E = 0;
        adj = (List<Edge>[]) new ArrayList[V];
        for (int v = 0; v < V; v++)
        {
            adj[v] = new ArrayList<Edge>();
        }
    }

    public EdgeWeightedGraph(EdgeWeightedGraph G)
    {
        this(G.V());
        this.E = G.E();
        for (int v = 0; v < G.V(); v++)
        {
            List<Edge> li = new ArrayList<Edge>();
            for (Edge e : G.adj[v])
            {
                li.add(e);
            }
            for (Edge e : li)
            {
                adj[v].add(e);
            }
        }
    }

    public int V()
    {
        return V;
    }

    public int E()
    {
        return E;
    }

    private void validateVertex(int v)
    {
        if (v < 0 || v >= V)
        {
            throw new IllegalArgumentException("顶点序号 " + v + " 不在 0 和 " + (V - 1) + "之间");
        }
    }

    /**
     * 添加边到无向非赋权图中
     * 
     * @param e
     * @author lhever 2017年2月19日 下午5:34:00
     * @since v1.0
     */
    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++;
    }

    /**
     * 返回顶点v的临接边
     * 
     * @param v
     * @return
     * @author lhever 2017年2月19日 下午5:35:09
     * @since v1.0
     */
    public Iterable<Edge> adj(int v)
    {
        validateVertex(v);
        return adj[v];
    }

    /**
     * 返回顶点v的度(邻接顶点数)
     * 
     * @param v
     * @return
     * @author lhever 2017年2月19日 下午5:36:08
     * @since v1.0
     */
    public int degree(int v)
    {
        validateVertex(v);
        return adj[v].size();
    }

    /**
     * 返回无向赋权图中的所有边
     * 
     * @return
     * @author lhever 2017年2月19日 下午5:38:55
     * @since v1.0
     */
    public Iterable<Edge> edges()
    {
        List<Edge> list = new ArrayList<Edge>();
        for (int v = 0; v < V; v++)
        {
            int selfLoops = 0;
            for (Edge e : adj(v))
            {
                // 无向图中,同一条边会出现在这条边的两个端点的邻接列表中,此处的条件 > 目的是避免重复查找
                if (e.other(v) > v)
                {
                    list.add(e);
                }
                // 对于自环,比如(5, 5, 0.8),添加边的时候会添加两次,但实际上只算一条边,所以此处只添加一条
                else if (e.other(v) == v)
                {
                    if (selfLoops % 2 == 0)
                    {
                        list.add(e);
                    }
                    selfLoops++;
                }
            }
        }
        return list;
    }

    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();
    }

    public static void main(String[] args)
    {

            //        0——————6
            //       /| \    |         
            //      / |  \   |         
            //     /  1   2  |         
            //    /          |         
            //   5———————————4        
            //    \         /         
            //     \       /          
            //      \     /
            //       \   /
            //         3

        EdgeWeightedGraph g = new EdgeWeightedGraph(7);

        Edge e1 = new Edge(0, 1, 0.7);
        g.addEdge(e1);

        Edge e2 = new Edge(0, 2, 4.5);
        g.addEdge(e2);

        Edge e3 = new Edge(0, 5, 5.0);
        g.addEdge(e3);

        Edge e4 = new Edge(0, 6, 3.1);
        g.addEdge(e4);

        Edge e5 = new Edge(5, 4, 2.9);
        g.addEdge(e5);

        Edge e6 = new Edge(6, 4, 7.8);
        g.addEdge(e6);

        Edge e7 = new Edge(3, 4, 9.7);
        g.addEdge(e7);

        Edge e8 = new Edge(3, 5, 6.0);
        g.addEdge(e8);

        System.out.println(g);


        EdgeWeightedGraph g1 = new EdgeWeightedGraph(g);
        System.out.println(g1);

    }

}

3 prim算法的延迟实现(添加新的横切边到树中的时候才能识别出失效的边)


import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

/**
 * 求无向非赋权图最小生成树的prim算法(延迟实现)
 * @author xxx 2017年2月19日 下午6:20:21
 * @version v1.0
 */
public class LazyPrimMST
{
    private static final double FLOATING_POINT_EPSILON = 1E-12;
    private double weight; // 最小生成树的权重
    private List<Edge> mst; // 最小生成树的所有边
    private boolean[] marked; // 如果顶点v已经被加入到最小生成树中, 则marked[v] = true
    private PriorityQueue<Edge> pq;

    public LazyPrimMST(EdgeWeightedGraph G)
    {
        mst = new ArrayList<Edge>();
        pq = new PriorityQueue<Edge>();
        marked = new boolean[G.V()];
        for (int v = 0; v < G.V(); v++)
        {
            if (!marked[v])
            {
                prim(G, v);
            }
        }
        assert check(G);
    }

    private void prim(EdgeWeightedGraph G, int s)
    {
        scan(G, s);
        while (!pq.isEmpty())
        { // 当最小生成树含有v- 1 条边的时候最好停止
          // 取出权值最小的边
            Edge e = pq.poll();
            int v = e.either(), w = e.other(v);
            assert marked[v] || marked[w];

            if (marked[v] && marked[w])
            {
                continue;
            }
            mst.add(e); // 边e属于最小生成树的边
            weight += e.weight();

            // 在运算过程中,最小生成树是逐渐生成的,此处将v添加到当前最小树中
            if (!marked[v])
            {
                scan(G, v);
            }
            // 在运算过程中,最小生成树是逐渐生成的,此处将v添加到当前最小树中
            if (!marked[w])
            {
                scan(G, w);
            }
        }
    }

    /**
     * 将顶点v的所有满足条件的邻接边添加到优先队列中,
     *      条件是: 这些邻接边的另外一个顶点还没有添加到当前的最小生成树中(还未被访问)
     * @param G
     * @param v
     * @author xxx 2017年2月19日 下午6:27:26
     * @since v1.0
     */
    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.add(e);
            }
        }
    }

    public Iterable<Edge> edges()
    {
        return mst;
    }

    public double weight()
    {
        return weight;
    }

    private boolean check(EdgeWeightedGraph G)
    {
        // 验证权重是否一致
        double totalWeight = 0.0;
        for (Edge e : edges())
        {
            totalWeight += e.weight();
        }
        if (Math.abs(totalWeight - weight()) > FLOATING_POINT_EPSILON)
        {
            System.err.printf("最小生成树的所有边的权值之和与 weight()方法计算的结果不一致: %f vs. %f\n", totalWeight, weight());
            return false;
        }

        // 利用连通查找算法判断是否是无环图
        UnionFind UnionFind = new UnionFind(G.V());
        for (Edge e : edges())
        {
            int v = e.either(), w = e.other(v);
            if (UnionFind.connected(v, w))
            {
                System.err.println("不是一个树森林");
                return false;
            }
            UnionFind.union(v, w);
        }

        // 判断是否是一颗生成树
        for (Edge e : G.edges())
        {
            int v = e.either(), w = e.other(v);
            if (!UnionFind.connected(v, w))
            {
                System.err.println("不是一棵生成树");
                return false;
            }
        }

        // 判断是否是一棵最小生成树
        for (Edge e : edges())
        {

            // 最小生成树中除e以外的所有边
            UnionFind = new UnionFind(G.V());
            for (Edge f : mst)
            {
                int x = f.either(), y = f.other(x);
                if (f != e)
                {
                    UnionFind.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 (!UnionFind.connected(x, y))
                {
                    if (f.weight() < e.weight())
                    {
                        System.err.println("边 " + f + "违背了切分条件");
                        return false;
                    }
                }
            }

        }
        return true;
    }

    public static void main(String[] args) 
    {
        //        0——————6
        //       /| \    |         
        //      / |  \   |         
        //     /  1   2  |         
        //    /          |         
        //   5———————————4        
        //    \         /         
        //     \       /          
        //      \     /
        //       \   /
        //         3

    EdgeWeightedGraph g = new EdgeWeightedGraph(7);

    Edge e1 = new Edge(0, 1, 0.7);
    g.addEdge(e1);

    Edge e2 = new Edge(0, 2, 4.5);
    g.addEdge(e2);

    Edge e3 = new Edge(0, 5, 5.0);
    g.addEdge(e3);

    Edge e4 = new Edge(0, 6, 3.1);
    g.addEdge(e4);

    Edge e5 = new Edge(5, 4, 2.9);
    g.addEdge(e5);

    Edge e6 = new Edge(6, 4, 7.8);
    g.addEdge(e6);

    Edge e7 = new Edge(3, 4, 9.7);
    g.addEdge(e7);

    Edge e8 = new Edge(3, 5, 6.0);
    g.addEdge(e8);
    LazyPrimMST mst = new LazyPrimMST(g);
    for (Edge e : mst.edges())
    {
        System.out.println(e);
    }
    System.out.printf("%.5f\n", mst.weight());
   }

}


///算法中用于连通查找的类的代码如下///

import java.util.Arrays;

public class UnionFind {

    private int[] parent;  // parent[i]的值为节点 i的父节点(或根节点)
    private byte[] rank;   // rank[i]的值为以i为根节点的子树的秩 
    private int count;     // 连通分量数


    /**
     * 初始化一个连通-查找算法的数据结构,N表示该数据结构中的节点(或称顶点、或称触电),并且,刚初始化
     * 的数据结构中,各个节点都位于各自的连通分量当中,也即N个连通分量
     * @param N
     * @author xxx 2017年2月28日 上午12:14:48
     * @since v1.0
     */
    public UnionFind(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;
        }
    }

    /**
     * 返回包含节点p的连通分量的id(或者是identifier)
     * @param p
     * @return
     * @author xxx 2017年2月28日 上午12:26:19
     * @since v1.0
     */
    public int find(int p)
    {
        validate(p);
        while (p != parent[p])
        {
            parent[p] = parent[parent[p]]; // 路径压缩
            p = parent[p];
        }
        return p;
    }


    /**
     * 返回连通分量的总数
     * @return
     * @author xxx 2017年2月28日 上午12:21:16
     * @since v1.0
     */
    public int count()
    {
        return count;
    }


    /**
     * 如果节点p和节点q位于同一个连通分量中,则返回true
     * @param p
     * @param q
     * @return
     * @author xxx 2017年2月28日 上午12:20:35
     * @since v1.0
     */
    public boolean connected(int p, int q)
    {
        return find(p) == find(q);
    }

    /**
     * 将包含节点p的连通分量与包含节点q的连通分量合并
     * @param p
     * @param q
     * @author xxx 2017年2月28日 上午12:19:24
     * @since v1.0
     */
    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--;//合并之后连通分量减一
    }

    private void validate(int p)
    {
        int N = parent.length;
        if (p < 0 || p >= N)
        {
            throw new IndexOutOfBoundsException("节点的大小必须位于0和 " + (N - 1) + "之间");
        }
    }


    public static void main(String[] args) {
        UnionFind uf = new UnionFind(4);
        if(!uf.connected(0, 3)) {
             uf.union(0, 3);
        }
        System.out.println(0 + " " + 3);
        System.out.println(uf.count() + " components");

        if(!uf.connected(0, 2)) {
         uf.union(0, 2);
       }
       System.out.println(0 + " " + 2);
       System.out.println(uf.count() + " components");
       System.out.println(Arrays.toString(uf.parent));
    }
}

4 Prim算法的即时实现


import java.util.ArrayList;
import java.util.List;


/**
 * 无向非赋权图的最小生成树算法
 * @author xxx 2017年2月19日 下午7:43:11
 * @version v1.0
 */
public class PrimMST {
    private static final double FLOATING_POINT_EPSILON = 1E-12;

    private Edge[] edgeTo;       
    private double[] distTo;      
    private boolean[] marked;     
    private IndexMinPQ<Double> pq;

    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++)
        {
            if (!marked[v])
            {
                prim(G, v);
            }
        }
        assert check(G);
    }

    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);
        }
    }

    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])
            {
                distTo[w] = e.weight();
                edgeTo[w] = e;
                if (pq.contains(w)) {
                    pq.decreaseKey(w, distTo[w]);
                }
                else {
                    pq.insert(w, distTo[w]);
                }
            }
        }
    }

    public Iterable<Edge> edges()
    {
        List<Edge> mst = new ArrayList<Edge>();
        for (int v = 0; v < edgeTo.length; v++)
        {
            Edge e = edgeTo[v];
            if (e != null)
            {
                mst.add(e);
            }
        }
        return mst;
    }

    public double weight()
    {
        double weight = 0.0;
        for (Edge e : edges())
        {
            weight += e.weight();
        }
        return weight;
    }

    private boolean check(EdgeWeightedGraph G)
    {

        double totalWeight = 0.0;
        for (Edge e : edges())
        {
            totalWeight += e.weight();
        }
        if (Math.abs(totalWeight - weight()) > FLOATING_POINT_EPSILON)
        {
            System.err.printf("最小生成树中所有边的权值和与weight()方法计算结果不一致");
            return false;
        }

        UnionFind UnionFind = new UnionFind(G.V());
        for (Edge e : edges())
        {
            int v = e.either(), w = e.other(v);
            if (UnionFind.connected(v, w))
            {
                System.err.println("不是一个树森林");
                return false;
            }
            UnionFind.union(v, w);
        }

        for (Edge e : G.edges())
        {
            int v = e.either(), w = e.other(v);
            if (!UnionFind.connected(v, w))
            {
                System.err.println("不是最小生成树");
                return false;
            }
        }

        for (Edge e : edges())
        {

            UnionFind = new UnionFind(G.V());
            for (Edge f : edges())
            {
                int x = f.either(), y = f.other(x);
                if (f != e)
                {
                    UnionFind.union(x, y);
                }
            }

            for (Edge f : G.edges())
            {
                int x = f.either(), y = f.other(x);
                if (!UnionFind.connected(x, y))
                {
                    if (f.weight() < e.weight())
                    {
                        System.err.println("边 " + f + " 违背了切分定理");
                        return false;
                    }
                }
            }

        }

        return true;
    }

   public static void main(String[] args)
   {
        //        0——————6
        //       /| \    |         
        //      / |  \   |         
        //     /  1   2  |         
        //    /          |         
        //   5———————————4        
        //    \         /         
        //     \       /          
        //      \     /
        //       \   /
        //         3

    EdgeWeightedGraph g = new EdgeWeightedGraph(7);

    Edge e1 = new Edge(0, 1, 0.7);
    g.addEdge(e1);

    Edge e2 = new Edge(0, 2, 4.5);
    g.addEdge(e2);

    Edge e3 = new Edge(0, 5, 5.0);
    g.addEdge(e3);

    Edge e4 = new Edge(0, 6, 3.1);
    g.addEdge(e4);

    Edge e5 = new Edge(5, 4, 2.9);
    g.addEdge(e5);

    Edge e6 = new Edge(6, 4, 7.8);
    g.addEdge(e6);

    Edge e7 = new Edge(3, 4, 9.7);
    g.addEdge(e7);

    Edge e8 = new Edge(3, 5, 6.0);
    g.addEdge(e8);

    PrimMST mst = new PrimMST(g);

    for (Edge e : mst.edges()) 
    {
        System.out.println(e);
    }

    System.out.printf("%.5f\n", mst.weight());
   }


}

///算法中使用到的IndexMinPQ.java代码如下//


import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 *  The <tt>IndexMinPQ</tt> class represents an indexed priority queue of generic keys.
 *  It supports the usual <em>insert</em> and <em>delete-the-minimum</em>
 *  operations, along with <em>delete</em> and <em>change-the-key</em> 
 *  methods. In order to let the client refer to keys on the priority queue,
 *  an integer between 0 and maxN-1 is associated with each key&mdash;the client
 *  uses this integer to specify which key to delete or change.
 *  It also supports methods for peeking at the minimum key,
 *  testing if the priority queue is empty, and iterating through
 *  the keys.
 *  <p>
 *  This implementation uses a binary heap along with an array to associate
 *  keys with integers in the given range.
 *  The <em>insert</em>, <em>delete-the-minimum</em>, <em>delete</em>,
 *  <em>change-key</em>, <em>decrease-key</em>, and <em>increase-key</em>
 *  operations take logarithmic time.
 *  The <em>is-empty</em>, <em>size</em>, <em>min-index</em>, <em>min-key</em>, and <em>key-of</em>
 *  operations take constant time.
 *  Construction takes time proportional to the specified capacity.
 *  <p>
 *  For additional documentation, see <a href="http://algs4.cs.princeton.edu/24pq">Section 2.4</a> of
 *  <i>Algorithms, 4th Edition</i> by Robert Sedgewick and Kevin Wayne.
 *
 *  @author Robert Sedgewick
 *  @author Kevin Wayne
 *
 *  @param <Key> the generic type of key on this priority queue
 */
public class IndexMinPQ<Key extends Comparable<Key>> implements Iterable<Integer> {
    private int maxN;        // maximum number of elements on PQ
    private int N;           // number of elements on PQ
    private int[] pq;        // binary heap using 1-based indexing
    private int[] qp;        // inverse of pq - qp[pq[i]] = pq[qp[i]] = i
    private Key[] keys;      // keys[i] = priority of i

    /**
     * Initializes an empty indexed priority queue with indices between <tt>0</tt>
     * and <tt>maxN - 1</tt>.
     * @param  maxN the keys on this priority queue are index from <tt>0</tt>
     *         <tt>maxN - 1</tt>
     * @throws IllegalArgumentException if <tt>maxN</tt> &lt; <tt>0</tt>
     */
    public IndexMinPQ(int maxN) {
        if (maxN < 0) throw new IllegalArgumentException();
        this.maxN = maxN;
        keys = (Key[]) new Comparable[maxN + 1];    // make this of length maxN??
        pq   = new int[maxN + 1];
        qp   = new int[maxN + 1];                   // make this of length maxN??
        for (int i = 0; i <= maxN; i++)
            qp[i] = -1;
    }

    /**
     * Returns true if this priority queue is empty.
     *
     * @return <tt>true</tt> if this priority queue is empty;
     *         <tt>false</tt> otherwise
     */
    public boolean isEmpty() {
        return N == 0;
    }

    /**
     * Is <tt>i</tt> an index on this priority queue?
     *
     * @param  i an index
     * @return <tt>true</tt> if <tt>i</tt> is an index on this priority queue;
     *         <tt>false</tt> otherwise
     * @throws IndexOutOfBoundsException unless 0 &le; <tt>i</tt> &lt; <tt>maxN</tt>
     */
    public boolean contains(int i) {
        if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException();
        return qp[i] != -1;
    }

    /**
     * Returns the number of keys on this priority queue.
     *
     * @return the number of keys on this priority queue
     */
    public int size() {
        return N;
    }

    /**
     * Associates key with index <tt>i</tt>.
     *
     * @param  i an index
     * @param  key the key to associate with index <tt>i</tt>
     * @throws IndexOutOfBoundsException unless 0 &le; <tt>i</tt> &lt; <tt>maxN</tt>
     * @throws IllegalArgumentException if there already is an item associated
     *         with index <tt>i</tt>
     */
    public void insert(int i, Key key) {
        if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException();
        if (contains(i)) throw new IllegalArgumentException("index is already in the priority queue");
        N++;
        qp[i] = N;
        pq[N] = i;
        keys[i] = key;
        swim(N);
    }

    /**
     * Returns an index associated with a minimum key.
     *
     * @return an index associated with a minimum key
     * @throws NoSuchElementException if this priority queue is empty
     */
    public int minIndex() {
        if (N == 0) throw new NoSuchElementException("Priority queue underflow");
        return pq[1];
    }

    /**
     * Returns a minimum key.
     *
     * @return a minimum key
     * @throws NoSuchElementException if this priority queue is empty
     */
    public Key minKey() {
        if (N == 0) throw new NoSuchElementException("Priority queue underflow");
        return keys[pq[1]];
    }

    /**
     * Removes a minimum key and returns its associated index.
     * @return an index associated with a minimum key
     * @throws NoSuchElementException if this priority queue is empty
     */
    public int delMin() {
        if (N == 0) throw new NoSuchElementException("Priority queue underflow");
        int min = pq[1];
        exch(1, N--);
        sink(1);
        assert min == pq[N+1];
        qp[min] = -1;        // delete
        keys[min] = null;    // to help with garbage collection
        pq[N+1] = -1;        // not needed
        return min;
    }

    /**
     * Returns the key associated with index <tt>i</tt>.
     *
     * @param  i the index of the key to return
     * @return the key associated with index <tt>i</tt>
     * @throws IndexOutOfBoundsException unless 0 &le; <tt>i</tt> &lt; <tt>maxN</tt>
     * @throws NoSuchElementException no key is associated with index <tt>i</tt>
     */
    public Key keyOf(int i) {
        if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException();
        if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
        else return keys[i];
    }

    /**
     * Change the key associated with index <tt>i</tt> to the specified value.
     *
     * @param  i the index of the key to change
     * @param  key change the key associated with index <tt>i</tt> to this key
     * @throws IndexOutOfBoundsException unless 0 &le; <tt>i</tt> &lt; <tt>maxN</tt>
     * @throws NoSuchElementException no key is associated with index <tt>i</tt>
     */
    public void changeKey(int i, Key key) {
        if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException();
        if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
        keys[i] = key;
        swim(qp[i]);
        sink(qp[i]);
    }

    /**
     * Change the key associated with index <tt>i</tt> to the specified value.
     *
     * @param  i the index of the key to change
     * @param  key change the key associated with index <tt>i</tt> to this key
     * @throws IndexOutOfBoundsException unless 0 &le; <tt>i</tt> &lt; <tt>maxN</tt>
     * @deprecated Replaced by {@link #changeKey(int, Key)}.
     */
    public void change(int i, Key key) {
        changeKey(i, key);
    }

    /**
     * Decrease the key associated with index <tt>i</tt> to the specified value.
     *
     * @param  i the index of the key to decrease
     * @param  key decrease the key associated with index <tt>i</tt> to this key
     * @throws IndexOutOfBoundsException unless 0 &le; <tt>i</tt> &lt; <tt>maxN</tt>
     * @throws IllegalArgumentException if key &ge; key associated with index <tt>i</tt>
     * @throws NoSuchElementException no key is associated with index <tt>i</tt>
     */
    public void decreaseKey(int i, Key key) {
        if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException();
        if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
        if (keys[i].compareTo(key) <= 0)
            throw new IllegalArgumentException("Calling decreaseKey() with given argument would not strictly decrease the key");
        keys[i] = key;
        swim(qp[i]);
    }

    /**
     * Increase the key associated with index <tt>i</tt> to the specified value.
     *
     * @param  i the index of the key to increase
     * @param  key increase the key associated with index <tt>i</tt> to this key
     * @throws IndexOutOfBoundsException unless 0 &le; <tt>i</tt> &lt; <tt>maxN</tt>
     * @throws IllegalArgumentException if key &le; key associated with index <tt>i</tt>
     * @throws NoSuchElementException no key is associated with index <tt>i</tt>
     */
    public void increaseKey(int i, Key key) {
        if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException();
        if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
        if (keys[i].compareTo(key) >= 0)
            throw new IllegalArgumentException("Calling increaseKey() with given argument would not strictly increase the key");
        keys[i] = key;
        sink(qp[i]);
    }

    /**
     * Remove the key associated with index <tt>i</tt>.
     *
     * @param  i the index of the key to remove
     * @throws IndexOutOfBoundsException unless 0 &le; <tt>i</tt> &lt; <tt>maxN</tt>
     * @throws NoSuchElementException no key is associated with index <t>i</tt>
     */
    public void delete(int i) {
        if (i < 0 || i >= maxN) throw new IndexOutOfBoundsException();
        if (!contains(i)) throw new NoSuchElementException("index is not in the priority queue");
        int index = qp[i];
        exch(index, N--);
        swim(index);
        sink(index);
        keys[i] = null;
        qp[i] = -1;
    }


   /***************************************************************************
    * General helper functions.
    ***************************************************************************/
    private boolean greater(int i, int j) {
        return keys[pq[i]].compareTo(keys[pq[j]]) > 0;
    }

    private void exch(int i, int j) {
        int swap = pq[i];
        pq[i] = pq[j];
        pq[j] = swap;
        qp[pq[i]] = i;
        qp[pq[j]] = j;
    }


   /***************************************************************************
    * Heap helper functions.
    ***************************************************************************/
    private void swim(int k) {
        while (k > 1 && greater(k/2, k)) {
            exch(k, k/2);
            k = k/2;
        }
    }

    private void sink(int k) {
        while (2*k <= N) {
            int j = 2*k;
            if (j < N && greater(j, j+1)) j++;
            if (!greater(k, j)) break;
            exch(k, j);
            k = j;
        }
    }


   /***************************************************************************
    * Iterators.
    ***************************************************************************/

    /**
     * Returns an iterator that iterates over the keys on the
     * priority queue in ascending order.
     * The iterator doesn't implement <tt>remove()</tt> since it's optional.
     *
     * @return an iterator that iterates over the keys in ascending order
     */
    public Iterator<Integer> iterator() { return new HeapIterator(); }

    private class HeapIterator implements Iterator<Integer> {
        // create a new pq
        private IndexMinPQ<Key> copy;

        // add all elements to copy of heap
        // takes linear time since already in heap order so no keys move
        public HeapIterator() {
            copy = new IndexMinPQ<Key>(pq.length - 1);
            for (int i = 1; i <= N; i++)
                copy.insert(pq[i], keys[pq[i]]);
        }

        public boolean hasNext()  { return !copy.isEmpty();                     }
        public void remove()      { throw new UnsupportedOperationException();  }

        public Integer next() {
            if (!hasNext()) throw new NoSuchElementException();
            return copy.delMin();
        }
    }


    /**
     * Unit tests the <tt>IndexMinPQ</tt> data type.
     */
    public static void main(String[] args) {
        // insert a bunch of strings
        String[] strings = { "it", "was", "the", "best", "of", "times", "it", "was", "the", "worst" };

        IndexMinPQ<String> pq = new IndexMinPQ<String>(strings.length);
        for (int i = 0; i < strings.length; i++) {
            pq.insert(i, strings[i]);
        }

        // delete and print each key
        while (!pq.isEmpty()) {
            int i = pq.delMin();
            StdOut.println(i + " " + strings[i]);
        }
        StdOut.println();

        // reinsert the same strings
        for (int i = 0; i < strings.length; i++) {
            pq.insert(i, strings[i]);
        }

        // print each key using the iterator
        for (int i : pq) {
            StdOut.println(i + " " + strings[i]);
        }
        while (!pq.isEmpty()) {
            pq.delMin();
        }

    }
}

/******************************************************************************
 *  Copyright 2002-2015, Robert Sedgewick and Kevin Wayne.
 *
 *  This file is part of algs4.jar, which accompanies the textbook
 *
 *      Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne,
 *      Addison-Wesley Professional, 2011, ISBN 0-321-57351-X.
 *      http://algs4.cs.princeton.edu
 *
 *
 *  algs4.jar is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  algs4.jar is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with algs4.jar.  If not, see http://www.gnu.org/licenses.
 ******************************************************************************/


5 另外一个联通查找算法的代码(后续用于Kruskal算法)

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class SetBasedUnionFind {

    private int count; // 连通分量数
    private Set<Integer>[] components;
    private int N;

    @SuppressWarnings("unchecked")
    public SetBasedUnionFind(int N) {
        if (N <= 0) {
            throw new IllegalArgumentException();
        }
        this.N = N;
        this.count = N;
        components = new Set[N];
    }

    public boolean connected(Integer i, Integer j) {
        if (components[i] != null && components[j] != null && components[i] == components[j]) {
            return true;
        }

        return false;
    }

    public void union(Integer i, Integer j)
    {
        validate(i);
        validate(j);
        Set<Integer> setI=components[i];
        Set<Integer> setJ=components[j];
        //边的两个顶点都未出现在其他集合中
        if(setI == null && setJ == null)
        {
            Set<Integer> set=new HashSet<Integer>();
            set.add(i);
            set.add(j);
            components[i] = set;
            components[j] = set;
        }//有一个顶点在其他集合中,一个不在,将不在的那个顶点集合合并进去
        else if(setI == null && setJ != null)
        {
            components[i] = setJ;
            setJ.add(i);
        }
        else if(setI != null && setJ == null)
        {
            components[j] = setI;
            setI.add(j);
        }//分别在不同的集合中,合并两个集合
        else if(setI != setJ)
        {
            for(int k: setI)
            {
                components[k] = setJ;
            }
            setJ.addAll(setI);
        } else {}//两个顶点在同一个结合中,会出现环路,舍弃

        count--;

    }

    public int count() 
    {
        return count;
    }

    private void  validate(Integer p) {
        if(p < 0 || p >= this.N) {
            throw new IllegalArgumentException();
        }
    } 

    public static void main(String... args) {

        SetBasedUnionFind uf = new SetBasedUnionFind(5);
        System.out.println(uf.count());
        System.out.println(uf.connected(0, 4));
        uf.getAllComponents();

        uf.union(0, 4);
        System.out.println(uf.count());
        System.out.println(uf.connected(0, 4));
        uf.getAllComponents();

        uf.union(0, 3);
        System.out.println(uf.count());
        System.out.println(uf.connected(0, 3));
        uf.getAllComponents();

        uf.union(1, 2);
        System.out.println(uf.count());
        System.out.println(uf.connected(1, 2));
        System.out.println(uf.connected(1, 0));
        uf.getAllComponents();

        uf.union(1, 4);
        System.out.println(uf.count());
        System.out.println(uf.connected(4, 2));
        uf.getAllComponents();
    }

    public Set[] getAllComponents() 
    {
        Set<Set<Integer>> result = new HashSet<Set<Integer>>();

        for(int i = 0; i < components.length; i++) {
            if(components[i] == null) {
                Set<Integer> set = new HashSet<Integer>();
                set.add(i);
                result.add(set);
            } else {
                result.add(components[i]);

            }
        }
        @SuppressWarnings("unchecked")
        Set<Integer>[] array = (Set[]) result.toArray(new HashSet[result.size()]);
        System.out.println(Arrays.toString(array));
        return array;
    }

/该算法的改进版//

import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

public class SetBasedUnionFind2 {

    private int count; // 连通分量数
    private Set<Integer>[] components;
    private int N;

    @SuppressWarnings("unchecked")
    public SetBasedUnionFind2(int N) {
        if (N <= 0) {
            throw new IllegalArgumentException();
        }
        this.N = N;
        this.count = N;
        components = new Set[N];
        for(int i = 0; i < components.length; i++) {
            components[i] = new HashSet<Integer>();
            components[i].add(i);
        }
    }

    public boolean connected(Integer i, Integer j) {
        if (components[i] == components[j]) {
            return true;
        }

        return false;
    }

    public void union(Integer i, Integer j)
    {
        validate(i);
        validate(j);
        Set<Integer> setI=components[i];
        Set<Integer> setJ=components[j];
        if(setI != setJ)
        {
            for(int k: setI)
            {
                components[k] = setJ;
            }
            setJ.addAll(setI);
        } else {}//两个顶点在同一个结合中,会出现环路,舍弃

        count--;

    }

    public int count() 
    {
        return count;
    }

    private void  validate(Integer p) {
        if(p < 0 || p >= this.N) {
            throw new IllegalArgumentException();
        }
    } 

    @SuppressWarnings("unchecked")
    public Set[] getAllComponents() 
    {
        Set<Set<Integer>> result = new HashSet<Set<Integer>>();

        for(int i = 0; i < components.length; i++) {
                result.add(components[i]);
        }
        Set<Integer>[] array = (Set[]) result.toArray(new HashSet[result.size()]);
        System.out.println(Arrays.toString(array));
        return array;
    }

    public static void main(String... args) 
    {

        SetBasedUnionFind2 uf = new SetBasedUnionFind2(5);
        System.out.println(uf.count());
        System.out.println(uf.connected(0, 4));
        uf.getAllComponents();

        uf.union(0, 4);
        System.out.println(uf.count());
        System.out.println(uf.connected(0, 4));
        uf.getAllComponents();

        uf.union(0, 3);
        System.out.println(uf.count());
        System.out.println(uf.connected(0, 3));
        uf.getAllComponents();

        uf.union(1, 2);
        System.out.println(uf.count());
        System.out.println(uf.connected(1, 2));
        System.out.println(uf.connected(2, 3));
        uf.getAllComponents();

        uf.union(1, 4);
        System.out.println(uf.count());
        System.out.println(uf.connected(4, 2));
        uf.getAllComponents();
    }





}

6 Kruskal算法的具体实现


import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
import edu.princeton.cs.algs4.UF;

/**
 * 无向赋权图最小生成树的Kruskal算法
 * @author xxx 2017年2月19日 下午9:08:59
 * @version v1.0
 */
public class KruskalMST
{
    private static final double FLOATING_POINT_EPSILON = 1E-12;

    private double weight;
    private List<Edge> mst = new ArrayList<Edge>();

    public KruskalMST(EdgeWeightedGraph G)
    {
        PriorityQueue<Edge> pq = new PriorityQueue<Edge>();
        for (Edge e : G.edges())
        {
            pq.add(e);
        }

        UF uf = new UF(G.V());
        while (!pq.isEmpty() && mst.size() < G.V() - 1)
        {
            Edge e = pq.poll();
            int v = e.either();
            int w = e.other(v);
            if (!uf.connected(v, w))
            {
                uf.union(v, w);
                mst.add(e);
                weight += e.weight();
            }
        }

        assert check(G);
    }

    /**
     * 返回最小生成树中的所有边
     * @return
     * @author xxx 2017年2月19日 下午9:09:37
     * @since v1.0
     */
    public Iterable<Edge> edges()
    {
        return mst;
    }

    /**
     * 返回最小生成树的权值
     * @return
     * @author xxx 2017年2月19日 下午9:10:10
     * @since v1.0
     */
    public double weight()
    {
        return weight;
    }

    private boolean check(EdgeWeightedGraph G)
    {
        double total = 0.0;
        for (Edge e : edges())
        {
            total += e.weight();
        }
        if (Math.abs(total - weight()) > FLOATING_POINT_EPSILON)
        {
            System.err.printf("最小生成树的所有边的权值和与weight()方法计算的结果不一致");
            return false;
        }

        // 验证最小生成树中是否含有环
        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("不是一个数森林");
                return false;
            }
            uf.union(v, w);
        }

        // 验证是否是一个生成树
        for (Edge e : G.edges())
        {
            int v = e.either(), w = e.other(v);
            if (!uf.connected(v, w))
            {
                System.err.println("不是一棵生成树");
                return false;
            }
        }

        for (Edge e : edges())
        {
            uf = new UF(G.V());
            for (Edge f : mst)
            {
                int x = f.either(), y = f.other(x);
                if (f != e)
                    uf.union(x, y);
            }

            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("边 " + f + " 违背了切分定理");
                        return false;
                    }
                }
            }

        }
        return true;
    }

    public static void main(String[] args)
    {
        EdgeWeightedGraph g = new EdgeWeightedGraph(7);

        Edge e1 = new Edge(0, 1, 0.7);
        g.addEdge(e1);

        Edge e2 = new Edge(0, 2, 4.5);
        g.addEdge(e2);

        Edge e3 = new Edge(0, 5, 5.0);
        g.addEdge(e3);

        Edge e4 = new Edge(0, 6, 3.1);
        g.addEdge(e4);

        Edge e5 = new Edge(5, 4, 2.9);
        g.addEdge(e5);

        Edge e6 = new Edge(6, 4, 7.8);
        g.addEdge(e6);

        Edge e7 = new Edge(3, 4, 9.7);
        g.addEdge(e7);

        Edge e8 = new Edge(3, 5, 6.0);
        g.addEdge(e8);
        KruskalMST mst = new KruskalMST(g);
        for (Edge e : mst.edges())
        {
            System.out.println(e);
        }
        System.out.printf("%.5f\n", mst.weight());
    }

}

7 Kruskal算法的另外一种实现


import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;

/**
 * 最小生成树的Kruskal算法,该类使用矩阵结构表示图
 * @author lhever 2017年2月28日 下午11:32:49
 * @version v1.0
 */
public class MatrixBasedKruskalMST
{

    private double weight = 0;// 权重
    private List<Edge> mst = new ArrayList<Edge>();// 最小生成树的所有边

    /**
     * 构造,在构造中完成计算
     * 
     * @param graph
     * @author lhever 2017年2月28日 下午11:34:01
     * @since v1.0
     */
    public MatrixBasedKruskalMST(double[][] graph)
    {
        int row = graph.length;
        int col = graph[0].length;
        if (row != col)
        {
            throw new IllegalArgumentException("传入的二维数组行值和列值必须相等");
        }

        int count = 0;
        PriorityQueue<Edge> edgeList = getEdgeList(graph);
        SetBasedUnionFind2 uf = new SetBasedUnionFind2(row);
        while (count < row - 1 && !edgeList.isEmpty())
        {
            Edge e = edgeList.poll();
            int v = e.either();
            int u = e.other(v);

            if (!uf.connected(v, u))
            {
                uf.union(v, u);
                mst.add(e);
                weight += e.weight();
                count++;
            }

        }
    }

    /**
     * 获取最小生成树的所有边
     * @return
     * @author lhever 2017年2月28日 下午11:48:57
     * @since v1.0
     */
    public List<Edge> edges()
    {
        return mst;
    }

    public double weight()
    {
        return weight;
    }

    /**
     * 获取图中的所有边
     * @param graph
     * @return
     * @author lhever 2017年2月28日 下午11:49:33
     * @since v1.0
     */
    private static PriorityQueue<Edge> getEdgeList(double[][] graph)
    {
        PriorityQueue<Edge> edgeList = new PriorityQueue<Edge>();
        int rlength = graph.length;
        int clength = graph[0].length;
        for (int i = 0; i < rlength; i++)
        {
            for (int j = i + 1; j < clength; j++)
            {
                if (graph[i][j] > 0 & graph[i][j] < Double.MAX_VALUE)
                {
                    Edge e = new Edge(i, j, graph[i][j]);
                    edgeList.add(e);
                }
            }
        }
        return edgeList;
    }

    public static void main(String[] args)
    {
        double graph[][] =
        {
                { 0, 1, 4, 4, 5 },
                { 1, 0, 3, 7, 5 },
                { 4, 3, 0, 6, Double.MAX_VALUE },
                { 4, 7, 6, 0, 2 },
                { 5, 5, Double.MAX_VALUE, 2, 0 } };

        MatrixBasedKruskalMST mst = new MatrixBasedKruskalMST(graph);

        List<Edge> edgeList = mst.edges();
        for (Edge e : edgeList)
        {
            System.out.println(e);
        }
        System.out.println("-------------------------------------------");

//                       原图表示为如下结构  

        //               ①  
        //            /  |  \  
        //           6   1   5  
        //          /    |    \  
        //        ②---5--③--5--④  
        //         \    / \    /  
        //          3  6   4  2  
        //           \/     \/  
        //           ⑤--6----⑥  
        double m = Double.MAX_VALUE;  
        double[][] weight = {{0, 0, 0, 0, 0, 0, 0},  
                {0, m, 6, 1, 5, m, m},  
                {0, 6, m, 5, m, 3, m},  
                {0, 1, 5, m, 5, 6, 4},  
                {0, 5, m, 5, m, m, 2},  
                {0, m, 3, 6, m, m, 6},  
                {0, m, m, 4, 2, 6, m}};//上图的矩阵  
        MatrixBasedKruskalMST mst1 = new MatrixBasedKruskalMST(weight);
        //最小生成树为:  
        //                ①  
        //                |     
        //                1      
        //                |       
        //        ②---5---③     ④  
        //         \      |    /  
        //          3     4   2  
        //           \    |  /
        //            \   | /
        //             ⑤  ⑥  
        //  
        List<Edge> edgeList1 = mst1.edges();
        for (Edge e : edgeList1)
        {
            System.out.println(e);
        }

    }

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值