一.prim算法
1.找到当前值最小的首结点(优先队列)
2.遍历所有邻居,更新他们的距离(只更新权重)(类似与dijkstra算法,更新距离时有区别)
3.当前结点出队
package LanQiao.MinDist;
import java.util.Comparator;
import java.util.List;
import java.util.PriorityQueue;
//最小生成树普里姆算法
//1.找到当前值最小的首结点(优先队列)
//2.遍历所有邻居,更新他们的距离(只更新权重)
//3.当前结点出队
public class prim {
static class vartex {
String name;
List<Edge> edges;
int dist = INF;
static final Integer INF = Integer.MAX_VALUE;
boolean visited;
public vartex(String name) {
this.name = name;
}
}
static class Edge {
int weight;
vartex linked;
public Edge(int weight, vartex linked) {
this.weight = weight;
this.linked = linked;
}
}
public static void main(String[] args) {
vartex v1 = new vartex("v1");
vartex v2 = new vartex("v2");
vartex v3 = new vartex("v3");
vartex v4 = new vartex("v4");
vartex v5 = new vartex("v5");
vartex v6 = new vartex("v6");
vartex v7 = new vartex("v7");
v1.edges = List.of(new Edge(2, v2), new Edge(1, v4), new Edge(4, v3));
v2.edges = List.of(new Edge(3, v4), new Edge(2, v1), new Edge(10, v5));
v3.edges = List.of(new Edge(4, v1), new Edge(2, v4), new Edge(5, v6));
v4.edges = List.of(new Edge(1, v1), new Edge(3, v2), new Edge(2, v3), new Edge(8, v6),
new Edge(4, v7), new Edge(7, v5));
v5.edges = List.of(new Edge(10, v2), new Edge(7, v4), new Edge(6, v7));
v6.edges = List.of(new Edge(5, v3), new Edge(8, v4), new Edge(1, v7));
v7.edges = List.of(new Edge(1, v6), new Edge(4, v4), new Edge(6, v5));
List<vartex> graph = List.of(v1, v2, v3, v4, v5, v6, v7);
prim(graph, v1);
}
private static void prim(List<vartex> graph, vartex source) {
PriorityQueue<vartex> queue = new PriorityQueue<>(Comparator.comparingInt(v -> v.dist));
for (vartex v : graph) {
queue.offer(v);//入队
}
source.dist = 0;
while (!queue.isEmpty()) {
// for (int i = 0; i < 6; i++) {
// queue.offer(graph.get(i));
// }
// System.out.println(queue);
//1.找到当前值最小的首结点
vartex peek = queue.peek();
//2.遍历所有邻居,更新他们的距离
for (Edge edge : peek.edges) {
vartex n = edge.linked;
int dist = edge.weight;
if (dist < n.dist)
n.dist = dist;
}
//3.当前结点出队
vartex poll = queue.poll();
poll.visited = true;
}
for (vartex v : graph) {
System.out.println(v.name + " " + v.dist);
}
}
}
二.kruskal
克鲁斯卡尔算法实现最小生成树
创建一个优先队列,把所有边加入,按照边权重大小依次出队,
并查集判断边的两段节点是否连通,连接后加入边集合
package LanQiao.MinDist; import java.util.ArrayList; import java.util.List; import java.util.PriorityQueue; //克鲁斯卡尔算法实现最小生成树 //创建一个优先队列,把所有边加入,按照边权重大小依次出队,并查集判断边的两段节点是否连通,连接后加入边集合 public class Kruskal { static class vartex{ String name; public vartex(String name) { this.name = name; } } static class Edge implements Comparable<Edge>{ List<vartex> vartexes; int start;//代表索引 int end;//代表索引 int weight; public Edge(List<vartex> vartexes, int start, int end, int weight) { this.vartexes = vartexes; this.start = start; this.end = end; this.weight = weight; } //Comparable接口根据边的权重从小到大 @Override public int compareTo(Edge o) { return Integer.compare(this.weight,o.weight); } @Override public String toString(){ return vartexes.get(start).name+" "+vartexes.get(end).name+" "+weight; } } public static void main(String[] args) { vartex v1=new vartex("v1"); vartex v2=new vartex("v2"); vartex v3=new vartex("v3"); vartex v4=new vartex("v4"); vartex v5=new vartex("v5"); vartex v6=new vartex("v6"); vartex v7=new vartex("v7"); List<vartex> vartexes=List.of(v1,v2,v3,v4,v5,v6,v7);//顶点集合 PriorityQueue<Edge> queue=new PriorityQueue<>(List.of(//Edge实现Comparable接口,优先队列根据边的权重实现排序 new Edge(vartexes,0,1,2), new Edge(vartexes,0,2,4), new Edge(vartexes,0,3,1), new Edge(vartexes,1,3,3), new Edge(vartexes,1 ,4,10), new Edge(vartexes,2 ,3,2), new Edge(vartexes, 2,5,5), new Edge(vartexes,3 ,4,7), new Edge(vartexes,3 ,5,8), new Edge(vartexes, 3,6,4), new Edge(vartexes, 4,6,6), new Edge(vartexes, 5,6,1) )); kruskal(queue,vartexes.size()); } private static void kruskal(PriorityQueue<Edge> queue, int size) { List<Edge> edges=new ArrayList<>();//edges用来收集找到的边 DisjointSet set =new DisjointSet(7);//并查集 // 给元素之间建立一种关系,通过find和union方法实现 while (edges.size()<size-1){ Edge poll = queue.poll(); int start = poll.start; int end = poll.end; int i = set.find(start); int j = set.find(end); if(start!=end){ set.union(i,j); edges.add(poll); } } for (Edge edge:edges){ System.out.println(edge.vartexes.get(edge.start).name+" "+edge.vartexes.get(edge.end).name+" "+edge.weight); } } }
三.并查集
并查集可以用来解决一些关于连通性的问题,例如在图论中判断节点之间是否连通,或者在计算机视觉中将图像的像素点划分成不同的区域
并查集包括两个主要操作:合并(Union)和查找(Find)。合并操作将两个集合合并为一个集合,查找操作用于查找某个元素所在的集合。并查集还可以使用路径压缩和按秩合并等优化策略来提高效率。
package LanQiao.MinDist;
import java.util.Arrays;
//并查集 find和union方法
//find 找出老大,值和下标相等
//union 在原来两个老大中选出新老夫,让两个集合相交
//find 优化 路径压缩
// public int find(int x){
// if(x==a[x]){
// return x;
// }
// return a[x]=find(a[x]);
// }
//union优化
//当有2个链接元素不等的集合需要连接时,让元素多的那一方当老大,提高整体查找老大的效率
public class DisjointSet {
int a[];//记录元素老大关系,最开始初始化为自己
int num[];//统计当前集合有多少元素相连接
public DisjointSet(int size) {
this.a = new int[size];
this.num=new int[size];
for (int i = 0; i < size; i++) {
a[i]=i;
num[i]=1;
}
}
//并查集 find和union方法
//找出老大,值和下标相等
//find 优化 路径压缩
public int find(int x){
if(x==a[x]){
return x;
}
return a[x]=find(a[x]);
}
public void union(int x,int y){
//在原来两个老大中选出新老夫,让两个集合相交
//优化后
if (num[x] > num[y]) {//x元素比y多,x当老大
a[y]=x;
num[x]=num[x]+num[y];//更新链接元素数目
}
else {
a[x]=y;
num[y]=num[y]+num[x];//更新连接元素数目
}
}
public static void main(String[] args) {
DisjointSet set=new DisjointSet(7);
int i= set.find(0);
int j= set.find(1);
// if(i!=j){
// set.union(i,j);
// }
// print(set);
// int i1 = set.find(5);
// int i2 = set.find(6);
// if(i1!=i2) {
// set.union(i1, i2);
// }
// print(set);
// int i3=set.find(1);
// int i4=set.find(5);
// if(i3!=i4)
// set.union(i3,i4);
// print(set);
//优化union
set.union(0,1);//节点数相同,默认y是老大
print(set);
set.union(1,2);//2只有一个元素,1有2个元素,2指向1的老大
print(set);
//6只有一个元素,与1链接时,指向1的老大1
//1的节点数为4
set.union(1,6);
print(set);
}
private static void print(DisjointSet set) {
System.out.println("关系");
for (int i = 0; i < set.a.length; i++) {
System.out.print(set.a[i]+" ");
}
System.out.println();
System.out.println("节点连接数目");
for (int i = 0; i < set.num.length; i++) {
System.out.print(set.num[i]+" ");
}
System.out.println();
}
}