最小生成树算法(Kruscal && Prim)
作者:互联网
`
输入第一行连个数字n,m代表结点数和道路数
下面m行,每行输入三个数字t1,t2,t3,代表序号为t1的结点与序号为t2的结点之间的距离是t3
求最小生成树的长度miniTree
`
Kruscal
核心思想是基于贪心策略,每次从路径中选择路径权值最小的一条边,并且每次都要判断这条边的两个结点的祖先是否都想同,如果不同(相同会出现环,故不能相同),则加入最小生成树集合中,直到加入的次数为n-1次,结束
解决方法
- 建立一个路径类,包含三个属性(结点1,结点2,两个结点之间的道路长度)
- 根据每次输入的t1,t2,t3,实例化相应的路径实例,每次创建一个,都要添加到list中
- 对list中的路径对象按照路径长度进行从小到大的排序
- 建立并查集函数,判断是否传入的两个结点序号是否来自一个祖先(防止环的出现)
- 建立Kruscal函数,建立一个变量用来更新每次新加入一条权值最短的路径后最小生成树长度的总和,迭代n-1次后,结束
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Scanner;
public class kruscal
{
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
int n = in.nextInt();//结点数
int m = in.nextInt();//道路数
List<Nodes> arr = new ArrayList<Nodes>();//存储路径对象
int[] f = new int[n+1];//存储并查集
//初始化并查集
for(int i=1;i<=n;i++) {
f[i] = i;//初始化为相应序号
}
//初始化arr数组
for(int i=1;i<=m;i++) {
int u = in.nextInt();
int v = in.nextInt();
int w = in.nextInt();
arr.add(new Nodes(u,v,w));
}
System.out.println(Kruscal(arr, m, n,f));//输出最小生成树长度
}
static int Kruscal(List<Nodes> arr,int m,int n,int[] f){
int miniSum = 0;//存储结果
int times = 0;//计数器
//根据w,对arr数组进行排序
Collections.sort(arr, new Comparator<Nodes>() {
public int compare(Nodes o1, Nodes o2) {
return o1.w - o2.w;
}});
for(int i=0;i<m;i++)//有n个结点,表示生成树的边数最多为n-1
{
if(merge(arr.get(i).u, arr.get(i).v, f)) {//如果不是同一个祖先
times++;
miniSum+=arr.get(i).w;
}
if(times == n-1) {//最多需要找出n-1条路,达到这个次数,就立即退出
break;
}
}
return miniSum;
}
//递归寻找祖先结点
static int getf(int x,int[] f){
if(f[x]==x) {
return x;
}else {
f[x] = getf(f[x],f);
return f[x];
}
}
//判断一条路的两个结点是否有同一个祖先,若有,该路径不能使用
static boolean merge(int x1,int x2,int[] f){
int t1 = getf(x1,f);
int t2 = getf(x2,f);
if(t1!=t2) {
f[t2] = t1;//如果没有祖先,就让t1点作为t2点的祖先
return true;
}
return false;
}
}
class Nodes {
int u;//结点1序号
int v;//结点2序号
int w;//结点1与结点2之间的权
Nodes(int u,int v,int w){
this.u = u;
this.v = v;
this.w = w;
}
}
Prim
基于动态规划思想,每次选择能够连接最小生成树,并且距离最短的的一个结点加入最小生成树集合
package 图论;
import java.util.Scanner;
public class Prim {
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
System.out.println("输入城市数量和道路数");
int n = in.nextInt();//城市数量
int m = in.nextInt();//城市之间的道路数
int[][] map = new int[n+1][n+1];
int[] book = new int[n+1];
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
map[i][j] = i==j?0:Integer.MAX_VALUE;
}
}
System.out.println("输入城市之间的信息");
for(int i = 1;i<=m;i++) {
int t1 = in.nextInt();
int t2 = in.nextInt();
int dis = in.nextInt();
map[t1][t2] = dis;
map[t2][t1] = dis;
}
System.out.println("输入起点");
int root = in.nextInt();
System.out.println(prim(root, map, book));
}
static int prim(int root,int[][] map,int[] book) {
int ans = 0;
int len = book.length;
int[] dis = new int[len];
//创建资源数组
for(int i = 1;i<len;i++) {
dis[i] = map[root][i];
//默认初始结点为结点1,初始化结点1到其他各个结点的距离
}
book[root] = 1;//标记root已经访问
for(int k=1;k<len;k++) {
int pos = 0;
int min = Integer.MAX_VALUE;
for(int i=1;i<len;i++) {
//每次选择没有访问的,并且距离最小生成树距离最短的点
if(book[i]==0 && dis[i]<min) {
min = dis[i];
pos = i;//标记序号
}
}
//开始调整
book[pos] = 1;
ans+=dis[pos];//加入最小生成树
for(int i = 1;i<len;i++) {
if(book[i]==0 && dis[i]>map[pos][i]) {
dis[i] = map[pos][i];
}
}
}
return ans;
}
}
标签:结点,Prim,Scanner,int,util,Kruscal,&&,import,new 来源: https://blog.csdn.net/qq_37756310/article/details/88628497