笔记来源:中国大学MOOC王道考研

一、概念

  • 连通图:图中任意两点都是连通的,那么图被称作连通图

  • 生成树:连通图包含全部顶点的一个极小连通子图

  • 最小生成树:在含有n个顶点的带权无向连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树(不一定唯一)。

    • 性质1:不一定唯一
    • 性质2:如果所有边的权重都不相同,则一定唯一
    • 性质3:如果连通图只有n-1条边,则最小生成树就是它本身
    • 性质4:最小生成树的边数为n-1

二、算法

2.1 Prim算法

步骤如下:

  1. 初始化,取任意顶点加入结果树:

  2. 加入A相邻的且不在结果树中,并且是最小权值的点C

  3. 加入与A,C相邻的且不在结果树中,并且是最小权值的点B(BC最小)

  4. 重复上述步骤,直到所有顶点都进入结果树:

java代码实现如下:

我们需要用两个数组来实现过程:

  • min_weight[n]:当前结果树到所有顶点的最短距离
  • adjvex[n]:adjvex[C]=0,代表C是通过A加入结果树的(0是A的下标)

  /*
* 首先我们给出图的存储结构
*/
package MST; import java.util.List; public class Graph { /*
* 点的存储
*/
private List<String> vex;
/*
* 边的存储
*/
private int edges[][]; public Graph(List<String> vex, int[][] edges) {
this.vex = vex;
this.edges = edges;
}
public List<String> getVex() {
return vex;
}
public void setVex(List<String> vex) {
this.vex = vex;
}
public int[][] getEdges() {
return edges;
}
public void setEdges(int edges[][]) {
this.edges = edges;
}
public int getVexNum() {
return vex.size();
}
public int getEdgeNum() {
return edges.length;
}
}

然后初始化图:

public class Prime {

	int m = Integer.MAX_VALUE;

	int[][] edges = {
{0, 3, 1, m, 4},
{3, 0, 2, m, m},
{1, 2, 0, 5, 6},
{m, m, 5, 0, m},
{4, m, 6, m, 0},
}; //打印最小生成树
void MST_Prime(Graph G) {
int vexNum = G.getVexNum();//节点个数
int[] min_weight = new int[vexNum];//当前结果树到所有顶点的最短距离
int[] adjvex = new int[vexNum];//adjvex[C]=0,代表C是通过A加入结果树的(0是A的下标)
/*初始化两个辅助数组*/
for(int i = 0; i < vexNum; i++) {
min_weight[i] = (G.getEdges())[0][i];//第一个顶点到其余顶点的距离
adjvex[i]=0;
}
int min_edg;//当前挑选的最小权值
int min_vex = 0;//最小权值对应的节点下标
/*循环剩余n-1个点*/
for(int i = 1; i < vexNum; i++) {
min_edg = Integer.MAX_VALUE;
for(int j = 1; j < vexNum; j++) {
if(min_weight[j]!=0 && min_weight[j] < min_edg) {
//寻找还没有被挑选进来的,最小权重的点
min_edg = min_weight[j];
min_vex = j;
}
}
min_weight[min_vex] = 0;//纳入结果树
/*修改对应辅助数组的值*/
for(int j = 0; j < vexNum; j++) {
if(min_weight[j]!=0 && (G.getEdges())[min_vex][j]<min_weight[j] && (G.getEdges())[min_vex][j]>0) {
min_weight[j] = (G.getEdges())[min_vex][j];
adjvex[j]=min_vex;
}
}
int pre = adjvex[min_vex];
int end = min_vex;
System.out.println("("+G.getVex().get(pre)+","+G.getVex().get(end)+")");
}
} //初始化图
Graph init() {
List<String> vex=new ArrayList<String>();
vex.add("A");
vex.add("B");
vex.add("C");
vex.add("D");
vex.add("E");
Graph graph = new Graph(vex, edges);
return graph;
} public static void main(String[] args) {
Prime prime = new Prime();
Graph graph = prime.init();
prime.MST_Prime(graph);
}
}

打印结果如下:

(A,C)

(C,B)

(A,E)

(C,D)

2.2 Kruskal算法

步骤如下:

  1. 每个顶点都是独立的树

  2. 挑选最短的边AC,加入边集中

  3. 依次加入BC,AB,但是AB构成了回路,舍弃

  4. 重复直到取了n-1条边

java代码实现如下:

使用 并查集堆排序kruskal算法

引用并查集博客:Java实现并查集

//首先我们实现并查集(用来判断是否构成回路--是否属于一个并查集)
public class UnionFindSet { //查询树的根
public static int find(int x, int [] par){
if(par[x] == x){
return x;
}else{
//压缩路径,第二次查询可以直接返回x的根而不用递归
return par[x] = find(par[x], par);
}
} //合并
public static void unite(int x, int y, int [] par, int [] rank){
x = find(x, par);
y = find(y, par); if(x == y){
return ;
} if(rank[x] < rank[y]){
par[x] = y;
}else{
par[y] = x;
if(rank[x] == rank[y]) rank[x]++;
}
} //判断x和y是否属于同一个集合
public static boolean same(int x, int y, int [] par){
return find(x, par) == find(y, par);
}
}

然后实现堆排序(稍作修改):

堆排序参考这篇博客:Java实现堆排序和图解

public class HeapSort {

   public static void sort(Edge[] arr){
//1.构建大顶堆
for(int i=arr.length/2-1;i>=0;i--){
//从第一个非叶子结点从下至上,从右至左调整结构
adjustHeap(arr,i,arr.length);
}
//2.调整堆结构+交换堆顶元素与末尾元素
for(int j=arr.length-1;j>0;j--){
swap(arr,0,j);//将堆顶元素与末尾元素进行交换
adjustHeap(arr,0,j);//重新对堆进行调整
} } /**
* 调整大顶堆(仅是调整过程,建立在大顶堆已构建的基础上)
* @param arr
* @param i
* @param length
*/
public static void adjustHeap(Edge[] arr,int i,int length){
Edge temp = arr[i];//先取出当前元素i
for(int k=i*2+1;k<length;k=k*2+1){//从i结点的左子结点开始,也就是2i+1处开始
if(k+1<length && arr[k].weight<arr[k+1].weight){//如果左子结点小于右子结点,k指向右子结点
k++;
}
if(arr[k].weight >temp.weight){//如果子节点大于父节点,将子节点值赋给父节点(不用进行交换)
arr[i] = arr[k];
i = k;
}else{
break;
}
}
arr[i] = temp;//将temp值放到最终的位置
} /**
* 交换元素
* @param arr
* @param a
* @param b
*/
public static void swap(Edge[] arr,int a ,int b){
Edge temp=arr[a];
arr[a] = arr[b];
arr[b] = temp;
}
}

最后我们实现Kruskal算法:

package MST;

import java.util.ArrayList;
import java.util.List; public class Kruskal { int m = Integer.MAX_VALUE; int[][] arr = {
{0, 3, 1, m, 4},
{3, 0, 2, m, m},
{1, 2, 0, 5, 6},
{m, m, 5, 0, m},
{4, m, 6, m, 0},
}; Graph init() {
List<String> vex=new ArrayList<String>();
vex.add("A");
vex.add("B");
vex.add("C");
vex.add("D");
vex.add("E");
Graph graph = new Graph(vex, arr);
return graph;
} //kruskal算法
void MST_Kruskal(Graph G, Edge[] edges, int[] parents, int[] rank) {
HeapSort.sort(edges);//堆排序
for(int i = 0; i < G.getEdgeNum(); i++) {
if(!UnionFindSet.same(edges[i].a, edges[i].b, parents)) {
UnionFindSet.unite(edges[i].a, edges[i].b, parents, rank);
System.out.println("("+G.getVex().get(edges[i].a)+","+G.getVex().get(edges[i].b)+")");
}
}
} public static void main(String[] args) {
Kruskal kruskal = new Kruskal();
Graph graph = kruskal.init();
int[] parents = {0,1,2,3,4};
int[] rank = {1,1,1,1,1};
Edge[] edges = new Edge[10];
int index = 0;
for(int i = 0; i < 5;i++) {
for(int j=0;j<i;j++) {
edges[index] = new Edge();
edges[index].weight = kruskal.arr[i][j];
edges[index].a = i;
edges[index++].b = j;
}
}
kruskal.MST_Kruskal(graph, edges, parents, rank);
}
}

输出结构为:

(C,A)

(C,B)

(E,A)

(D,C)

最小生成树的java实现的更多相关文章

  1. 图的最小生成树(java实现)

    1.图的最小生成树(贪心算法) 我两个算法的输出都是数组表示的,当前的索引值和当前索引对应的数据就是通路,比如parent[2] = 5;即2和5之间有一个通路,第二个可能比较好理解,第一个有点混乱 ...

  2. 最小生成树之kruskal方法实现 (java)

    今天是个阴天,下了点雨,work ......... 步骤:将所有边排序,然后不断从小到大加上边,这个过程最重要的是避免环的产生,此处用并查集.(nyoj 38) package 最小生成树; imp ...

  3. java实现最小生成树的prim算法和kruskal算法

    在边赋权图中,权值总和最小的生成树称为最小生成树.构造最小生成树有两种算法,分别是prim算法和kruskal算法.在边赋权图中,如下图所示: 在上述赋权图中,可以看到图的顶点编号和顶点之间邻接边的权 ...

  4. 【惊喜】Github爆火的java面试神技+java核心面试技术已开发下载,大厂内都传疯了!

    前言 今年,由于疫情的影响,很多互联网企业都在缩减招聘成本.作为程序员,原本这两年就面临竞争激烈.年龄危机的问题,而现在的求职局面又完全是企业在挑人的状态. 所以最好能在空闲的时候看看大厂相匹配的技术 ...

  5. Java Web学习之路

    编程基础 1-1 常用数据结构 数组.链表.堆.栈.队列.Hash表.二叉树等1-2 算法思想 算法时间复杂度和空间复杂度的分析计算 1-2 算法思想:递推.递归.穷举.贪心.分治.动态规划.迭代.分 ...

  6. Spark案例分析

    一.需求:计算网页访问量前三名 import org.apache.spark.rdd.RDD import org.apache.spark.{SparkConf, SparkContext} /* ...

  7. 数据结构-图-Java实现:有向图 图存储(邻接矩阵),最小生成树,广度深度遍历,图的连通性,最短路径1

    import java.util.ArrayList; import java.util.List; // 模块E public class AdjMatrixGraph<E> { pro ...

  8. 图的存储,搜索,遍历,广度优先算法和深度优先算法,最小生成树-Java实现

    1)用邻接矩阵方式进行图的存储.如果一个图有n个节点,则可以用n*n的二维数组来存储图中的各个节点关系. 对上面图中各个节点分别编号,ABCDEF分别设置为012345.那么AB AC AD 关系可以 ...

  9. java用Kruskal实现最小生成树

    今天更新这篇文章超级激动,因为我会最小生成树的算法了(其实昨天就开始研究了,只是昨天参加牛客网的算法比赛,结果又被虐了,好难过~) 最小生成树的算法,其实学了数据结构就会有一定的基础,Kruskal算 ...

随机推荐

  1. Python多核编程mpi4py实践及并行计算-环境搭建篇

    1.安装python,这个没什好说的,直接装就行 2.做并行计算.数据挖掘,机器学习等一般都要用的numpy,这个在Windows版本上安装有点问题,安装比较麻烦,建议在linux上搭建环境 3.安装 ...

  2. 三文搞懂学会Docker容器技术(上)

    1,Docker简介 1.1 Docker是什么? Docker官网: https://www.docker.com/ Docker 是一个开源的应用容器引擎,基于 Go 语言 并遵从Apache2. ...

  3. 为Promise添加finally方法支持,把小程序函数变成promise函数

    // 为Promise添加finally方法支持 Promise.prototype.finally = function (callback) {   let P = this.constructo ...

  4. ObjectOutputStream和ObjectInputStream对对象进行序列化和反序列化

    1 Java序列化和反序列化简介 Java序列化是指把对象转换为字节序列的过程,而Java反序列化是指把字节序列恢复为java对象的过程. 我们把对象序列化成有序字节流,保存到本地磁盘或者Redis等 ...

  5. 利用EasyMock生成数据库连接简单测试示例

    package demo.mock; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.Re ...

  6. 平时自己常用的git指令

    增删改查 创建标签 $ git tag -a v1.4 -m 'my version 1.4' 用 -a (译注:取 annotated 的首字母)指定标签名字即可 -m 选项则指定了对应的标签说明 ...

  7. YoyoGo基于ASP.NET Core设计的Golang实现

    YoyoGo YoyoGo 是一个用 Go 编写的简单,轻便,快速的 微服务框架,目前已实现了Web框架的能力,但是底层设计已支持. Github https://github.com/yoyofx/ ...

  8. Double值保留两位小数的四种方法

    public class DoubleTest { //保留两位小数第三位如果大于4会进一位(四舍五入) double f = 6.23556; /** *使用精确小数BigDecimal */ pu ...

  9. (win7) 在IIS6.0 中配置项目

    1.进入IIS管理器 右击“计算机”->管理->服务和应用程序->Internet信息服务(IIS)管理器 2.将项目加入IIS中 网站->右击“默认网站”->添加虚拟目 ...

  10. 二.4vue展示用户数据及用户组操作以及给用户组添加额外字段

    一.用户列表 1.新建(1)views/users/index.vue: <template> <div class="user-list-container"& ...