(┬_┬),终于…
参考文章:
算法☞《图》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
测试结果