无向图实现+Kruskal+prim延时+prim即时(索引优先队列)Java实现

(┬_┬),终于…
参考文章:
算法☞《图》java实现
索引优先队列

本文主要是实现《算法》一书中关于无向图的部分内容,并加以测试;
主要功能类(一个类用一个.java文件):

Edge 边类

/**
 * FileName: Edge
 * Author:   Jerry
 * Date:     2020/2/11 20:37
 * Description: 带权重的边的数据类型
 */

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

    /**
     * 得到边的一个顶点
     *
     * @return
     */
    public int either() {
        return v;
    }

    public int other(int vertex) {
        if (vertex == v) return w;
        else if (vertex == w) return v;
        else throw new RuntimeException("Inconsisitent edge");
    }

    /**
     * 自定义比较,根据边的权重
     *
     * @param o
     * @return
     */

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

    @Override
    public String toString() {
        return "Edge{" +
                "v=" + v +
                ",w=" + w +
                ",weight="
                + weight + '}';
    }
}

EdgeWeightedGraph 加权无向图

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

/**
 * FileName: EdgeWeightedGraph
 * Author:   Jerry
 * Date:     2020/2/11 20:50
 * Description: 加权无向图的数据类型
 */

public class EdgeWeightedGraph {
    //顶点总数
    private final int V;
    //边的总数
    private int E;
    //邻接表
    private List<Edge>[]adj;

    public EdgeWeightedGraph(int V){
        this.V =V;
        this.E =0;
        adj = new ArrayList[V];
        for(int i=0;i<V;i++){
            adj[i]=new ArrayList<Edge>();
        }

    }

    public int V(){
        return V;
    }

    /**
     * 返回边的个数
     * @return
     */
    public int E(){
        return E;
    }

    /**
     * 加边
     * @param e
     */
    public void addEdge(Edge e){
        int v=e.either();
        int w=e.other(v);
        adj[v].add(e);
        adj[w].add(e);
        E++;
    }

    /**
     * 返回与v向临的所有结点
     * @param v
     * @return
     */
    public Iterable<Edge> adj(int v){
        return adj[v];
    }

    /**
     * 返回图中的所有边
     * @return
     */
    public Iterable<Edge> edges(){
        List<Edge> list = new ArrayList<>();
        for(int i=0;i<V;i++){
            for(Edge e:adj[i]){
                /**
                 * 对于一条边 e,连接两个点i和j;同时在邻接表中存在
                 * 另一条相同的边f,对应连接两个点j和i
                 */
                if(e.other(i)>i){
                    list.add(e);
                }
            }
        }
        return list;
    }


}

KruskalMST Kruskal求MST

import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;

/**
 * FileName: KruskalMST
 * Author:   Jerry
 * Date:     2020/2/12 11:35
 * Description: Kruskal求最小生成树
 */

public class KruskalMST {
    //优先队列
    private PriorityQueue<Edge> pq;
    /**
     * 最小生成树
     */
    private Queue<Edge> mst;

    public KruskalMST(EdgeWeightedGraph graph){
        pq=new PriorityQueue<>();
        mst = new LinkedList<>();
        //将边加入优先队列
        for(Edge edge:graph.edges()){
            pq.add(edge);
        }
        mst(graph);
    }

    private void mst(EdgeWeightedGraph graph){
        WeightedQuickUnionUF uf = new WeightedQuickUnionUF(graph.V());
        while (!pq.isEmpty()){
            //从里面取出最小的元素
            Edge e = pq.poll();
            int v = e.either();
            int w = e.other(v);
            //防止成环
            if(uf.connected(v,w)){
                continue;
            }
            uf.union(v,w);
            mst.offer(e);
        }
    }

    public Queue<Edge> getMst(){
        return mst;
    }

}

WeightedQuickUnionUF 加权quick-union,配合Kruskal

/**
 * FileName: WeightedQuickUnionUF
 * Author:   Jerry
 * Date:     2020/2/12 11:13
 * Description: 加权quick-union算法
 */

public class WeightedQuickUnionUF {
    private int[]id;//父链接数组
    private int[]size;//(由触点索引的)各个根节点所对应的分量大小
    private int count;//连通分量的数量

    /**
     * 构造函数
     * @param N
     */
    public WeightedQuickUnionUF(int N){
        count =N;
        id = new int[N];
        for(int i=0;i<N;i++){
            id[i]=i;
        }
        size=new int[N];
        for(int i=0;i<N;i++){
            size[i]=1;
        }

    }

    /**
     * p和q是否连接
     * @param p
     * @param q
     * @return
     */
    public boolean connected(int p,int q){
        return find(p)==find(q);
    }

    /**
     * 找到根节点
     * @param v
     * @return
     */
    public int find(int v){
        //在根节点中id[v]=v
        while(id[v]!=v){
            v=id[v];
        }
        return v;
    }

    public void union(int p,int q){
        int i=find(p);
        int j=find(q);
        //如果在一个连通分量中,返回
        if(i==j)
            return;
        //将小树加入大树
        if(size[i]<size[j]){
            id[i]=j;
            size[j]+=size[i];
        }else{
            id[j]=i;
            size[i]+=size[j];
        }
        count--;
    }

}

LazyPrimMST

import java.util.LinkedList;
import java.util.PriorityQueue;
import java.util.Queue;

/**
 * FileName: LazyPrimMST
 * Author:   Jerry
 * Date:     2020/2/12 12:27
 * Description: prim算法延时实现
 */

public class LazyPrimMST {
    private boolean[]marked;
    private Queue<Edge> mst;
    /**
     * 横切边
     */
    private PriorityQueue<Edge> pq;

    public LazyPrimMST(EdgeWeightedGraph graph){
        marked = new boolean[graph.V()];
        pq = new PriorityQueue<>();
        mst = new LinkedList<>();
        mst(graph);
    }

    /**
     * 最小生成树算法
     * @param graph
     */
    public void mst(EdgeWeightedGraph graph){
        visit(graph,0);
        while(!pq.isEmpty()){
            Edge e = pq.poll();
            int v = e.either();
            int w = e.other(v);
            //如果两个顶点都被标记了,则看下一条边
            if(marked[v]&&marked[w]){
                continue;
            }
            //将边加入mst
            mst.add(e);
            if(!marked[v]){
                visit(graph,v);
            }
            if(!marked[w]){
                visit(graph,w);
            }
        }
    }
    /**
     * 访问点v
     * @param graph
     * @param v
     */
    public void visit(EdgeWeightedGraph graph,int v){
        marked[v]=true;
        /**
         * 加入v的横切边进入优先队列
         */
        for(Edge e:graph.adj(v)){
            if(!marked[e.other(v)]){
                pq.add(e);
            }
        }
    }

    /**
     * 返回最小生成树
     * @return
     */
    public Queue<Edge> getMst(){
        return this.mst;
    }
}

PrimMST

import java.util.ArrayList;

/**
 * FileName: PrimMST
 * Author:   Jerry
 * Date:     2020/2/12 14:40
 * Description: Prim算法即时版本
 */

public class PrimMST {
    //距离树最近的边
    private Edge[] edgeTo;//某节点距离树最近的边,key=id;
    private double[] distTo;//某节点距离树的最短距离,key=id;
    private boolean[] marked;//结点是否已经加入MST
    /**
     * 有效的横切边,没有效的都被删除
     * value:权重
     */
    private IndexMinPQ<Double> pq;

    public PrimMST(EdgeWeightedGraph graph){
        edgeTo = new Edge[graph.V()];
        distTo = new double[graph.V()];
        marked = new boolean[graph.V()];
        //IndexMinPQ是索引优先队列,从1开始
        pq = new IndexMinPQ<Double>(graph.V());
        //初始化结点到树的距离为无限大
        for(int i=0;i<graph.V();i++){
            distTo[i]=Double.POSITIVE_INFINITY;
        }
        pq.insert(0+1,0.0);//插入第一个点0
        while(!pq.isEmpty()){
            visit(graph,pq.delMin()-1);//返回最小横切边对应的结点
        }
    }

    public void visit(EdgeWeightedGraph graph,int v){
        marked[v]=true;
        //遍历与v相连的节点
        for(Edge e:graph.adj(v)){
            int w = e.other(v);
            //如果w已经是最小树的节点,则不进行处理
            if(marked[w]){
                continue;
            }
            //假如w结点的权重小于w结点到树的距离
            if(e.weight()<distTo[w]){
                //更新横切边
                edgeTo[w]=e;
                //更新为e边的权重
                distTo[w]=e.weight();
                //将结点w放入优先队列
                if(pq.contains(w+1)){
                    pq.changeKey(w+1,distTo[w]);
                }
                else{
                    pq.insert(w+1,distTo[w]);
                }
            }
        }

    }

    public ArrayList<Edge> mst(){
        ArrayList<Edge> list = new ArrayList<>();
        for(Edge e:edgeTo){
            if(e!=null){
                list.add(e);
            }
        }
        return list;
    }
}

Main

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

/**
 * FileName: Main
 * Author:   Jerry
 * Date:     2020/2/11 21:49
 * Description: 测试功能
 */

public class Main {
    public static void main(String []args){
        String path ="src/tinyEWG.txt";
        try{
            FileReader file = new FileReader(path);
            EdgeWeightedGraph graph = null;
            graph = createGraph(file);
            //打印图中的所有边
            System.out.println("打印图中的所有的边:");
            Iterable<Edge> iter = graph.edges();
            for(Edge e:iter){
                int v=e.either();
                int w =e.other(v);
                double weight =e.weight();
                System.out.println("v: "+v+" w: "+w+" weight: "+weight);
            }
            System.out.println("打印最小生成树,kruskalMST:");
            iter = new KruskalMST(graph).getMst();
            for(Edge e:iter){
                int v = e.either();
                int w = e.other(v);
                double weight =e.weight();
                System.out.println(v+" → "+w+String.format(" weight:%.2f",weight));
            }
            System.out.println("打印最小生成树,Prim延时:");
            iter = new LazyPrimMST(graph).getMst();
            for(Edge e:iter){
                int v = e.either();
                int w = e.other(v);
                double weight =e.weight();
                System.out.println(v+" → "+w+String.format(" weight:%.2f",weight));
            }
            System.out.println("打印最小生成树,Prim即时:");
            iter = new PrimMST(graph).mst();
            for(Edge e:iter){
                int v = e.either();
                int w = e.other(v);
                double weight =e.weight();
                System.out.println(v+" → "+w+String.format(" weight:%.2f",weight));
            }


        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static EdgeWeightedGraph createGraph(FileReader file){
        BufferedReader bf = new BufferedReader(file);
        EdgeWeightedGraph graph =null;
        try{
            String v = bf.readLine();
            int V =Integer.parseInt(v);
            graph=new EdgeWeightedGraph(V);
            String e =bf.readLine();
            int E = Integer.parseInt(e);
            String []sp=null;
            for(int i=0;i<E;i++){
                String s = bf.readLine();
                sp=s.split(" ");
                int a=Integer.parseInt(sp[0]);
                int b = Integer.parseInt(sp[1]);
                double weight = Double.parseDouble(sp[2]);
                Edge edge = new Edge(a,b,weight);
                graph.addEdge(edge);
            }


        } catch (IOException e) {
            e.printStackTrace();
        }
        return graph;
    }
}

tinyEWG.txt

8
16
4 5 .35
4 7 .37
5 7 .28
0 7 .16
1 5 .32
0 4 .38
2 3 .17
1 7 .19
0 2 .26
1 2 .36
1 3 .29
2 7 .34
6 2 .40
3 6 .52
6 0 .58
6 4 .93

测试结果

测试结果1
测试结果2

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值