最近在学习算法,在学习最小生成树的过程中,感觉算法思想很简单,但实现起来对于我这样的菜鸟来说有些困难,后来上网搜了一些文章,发现这篇讲得很清楚,与大家分享一下
http://blog.csdn.net/fengchaokobe/article/details/7521780。我着重研究了一下克鲁斯卡尔算法。
克鲁斯卡尔算法
克鲁斯卡尔算法的思想如下:
克鲁斯卡尔算法的核心思想是:在带权连通图中,不断地在边集合中找到最小的边,如果该边满足得到最小生成树的条件,就将其构造,直到最后得到一颗最小生成树。
克鲁斯卡尔算法的执行步骤:
第一步:在带权连通图中,将边的权值排序;
第二步:判断是否需要选择这条边(此时图中的边已按权值从小到大排好序)。判断的依据是边的两个顶点是否已连通,如果连通则继续下一条;如果不连通,那么就选择使其连通。
第三步:循环第二步,直到图中所有的顶点都在同一个连通分量中,即得到最小生成树。
在一条新边加入时,要判断是否形成环路(其实前两条边不用判断,大家明白的哈),那么该如何判断呢?
1.如果没有形成回路,那么直接将其连通。此时,对于边的集合又要做一次判断:这两个点是否在已找到点的集合中出现过?
①.如果两个点都没有出现过,那么将这两个点都加入已找到点的集合中;
②.如果其中一个点在集合中出现过,那么将另一个没有出现过的点加入到集合中;
③.如果这两个点都出现过,则不用加入到集合中。
2.如果形成回路,不符合要求,直接进行下一次操作。
通过生成的过程可以看出,能否得到最小生成树的核心问题就是上面所描述的判断法则。那么,我们如何用算法来描述判断法则呢?我认为只需要三个步骤即可:
⒈将某次操作选择的边XY的两个顶点X和Y和已找到点的集合作比较,如果
①.这两个点都在已找到点的集合中,那么return 2;
②.这两个点有一个在已找到点的集合中,那么return 1;
③这两个点都不在一找到点的集合中,那么return 0;
⒉当返回值为0或1时,可判定不会形成回路;
⒊当返回值为2时,判定能形成回路的依据是:假如能形成回路,设能形成回路的点的集合中有A,B,C,D四个点,那么以点A为起始点,绕环路一周后必能回到点A。如果能回到,则形成回路;如果不能,则不能形成回路。
具体的流程在我给出的链接中英讲得很好了,在这里我就不赘述了。
在判断是否出现环路时,我采用了如下方法:
转自http://blog.csdn.net/lyflower/archive/2008/03/01/2137710.aspx。
如果存在回路,则必存在一个子图,是一个环路。环路中所有顶点的度>=2。
n算法:
第一步:删除所有度<=1的顶点及相关的边,并将另外与这些边相关的其它顶点的度减一。
第二步:将度数变为1的顶点排入队列,并从该队列中取出一个顶点重复步骤一。
如果最后还有未删除顶点,则存在环,否则没有环。
n算法分析:
由于有m条边,n个顶点。如果m>=n,则根据图论知识可直接判断存在环路。
(证明:如果没有环路,则该图必然是k棵树 k>=1。根据树的性质,边的数目m = n-k。k>=1,所以:m<n)
如果m<n 则按照上面的算法每删除一个度为0的顶点操作一次(最多n次),或每删除一个度为1的顶点(同时删一条边)操作一次(最多m次)。这两种操作的总数不会超过m+n。由于m<n,所以算法复杂度为O(n)
(这种判断环路的方法很简单吧)
下面我贴出我的代码,希望大牛们不要喷我,我只是实现了找出最小生成树的功能,具体效率方面没有考虑(ps:只针对于无向图)
import java.util.Scanner;
//存储边,及边的结点
class dataType{
int value;
String startNode;
String endNode;
}
//存储结点,flag用于标记结点是否已经加入到最小生成树中
class dataType1{
String node;
int flag;
}
public class KruskalAlgorithm {
//对边进行排序
static void sort(dataType[] arr,int n)
{
int temp;
String temp1;
for(int i=0;i<n;i++)
{
int flag=0;
for(int j = 0;j<n-i-1;j++)
{
if(arr[j].value>arr[j+1].value)
{
temp = arr[j].value;
arr[j].value = arr[j+1].value;
arr[j+1].value = temp;
temp1=arr[j].startNode;
arr[j].startNode=arr[j+1].startNode;
arr[j+1].startNode=temp1;
temp1=arr[j].endNode;
arr[j].endNode=arr[j+1].endNode;
arr[j+1].endNode=temp1;
flag++;
}
}
if(flag==0)
break;
}
}
//判断能否出现回路
static int isLoop(dataType1 arr1[],int num, int edgeNum)
{
int nodeFlag=0;
for(int i=0;i<num;i++)
{
if(arr1[i].flag==1)
{
nodeFlag++;
}
}
if(nodeFlag>(edgeNum+1))
return 0;
else
return 1;
}
//判断选择的一条边中有几个顶点已经出现过
static int isEmerge(String startNode,String endNode,dataType1[] arr1,int num)
{
int loop = 0;
for(int i = 0;i<num;i++)
{
if(arr1[i].node.compareTo(startNode)==0)
{
if(arr1[i].flag==1)
{
loop++;
}
else
{
arr1[i].flag=1;
}
}
if(arr1[i].node.compareTo(endNode)==0)
{
if(arr1[i].flag==1)
{
loop++;
}
else
{
arr1[i].flag=1;
}
}
}
/*for(int i=0;i<num;i++)
{
System.out.print("("+arr1[i].node+","+arr1[i].flag+") ");
}
System.out.println();*/
return loop;
}
public static void main(String[] args) {
int sum=0;
int edgeNum = 0;
System.out.print("请输入图中边的条数:");
Scanner input = new Scanner(System.in);
int n = input.nextInt();
System.out.print("请输入图中点的个数:");
int num = input.nextInt();
dataType1[] arr1 = new dataType1[num];
System.out.print("请输入图中的点:");
for(int j = 0;j<num;j++)
{
arr1[j] = new dataType1();
arr1[j].node=input.next();
arr1[j].flag = 0;
}
int i=0;
int j;
dataType[] arr = new dataType[n];
while(true)
{
if(i<n)
{
System.out.print("请输入边的长度,以及边的两个顶点");
arr[i] = new dataType();
j = input.nextInt();
if(j==0)
{
break;
}else
{
arr[i].value = j;
arr[i].startNode = input.next();
arr[i].endNode = input.next();
i++;
}
}
else
{
break;
}
}
System.out.println("开始对边进行排序:");
sort(arr,n);
System.out.println("完成排序:");
for(i = 0;i<n;i++)
{
System.out.print("("+arr[i].value+","+arr[i].startNode+","+arr[i].endNode+") ");
}
System.out.println();
for(int k =0;k<n;k++)
{
if(edgeNum<num)
{
if(k<2)//加入的第一条边、第二条边不用判断是否形成环路
{
int loop = isEmerge(arr[k].startNode,arr[k].endNode,arr1,num);//将边的结点加入到已选集合
sum=sum+arr[k].value;
edgeNum++;
System.out.print("("+arr[k].startNode+","+arr[k].endNode+") ");
}else{
int loop = isEmerge(arr[k].startNode,arr[k].endNode,arr1,num);
if(loop==0)
{
sum = sum+arr[k].value;
edgeNum++;
System.out.print("("+arr[k].startNode+","+arr[k].endNode+") ");
}
if(loop==1)
{
sum = sum+arr[k].value;
edgeNum++;
System.out.print("("+arr[k].startNode+","+arr[k].endNode+") ");
}
if(loop==2)
{
if(isLoop(arr1,num,edgeNum)==0)
{
sum = sum+arr[k].value;
edgeNum++;
System.out.print("("+arr[k].startNode+","+arr[k].endNode+") ");
}
}
}
}else
{
break;
}
}
System.out.print("最小生成树的长度为:"+sum);
}
}
分享到:
相关推荐
最小生成树总结.cpp
最小生成树是图论中的一个重要概念,特别是在解决网络优化问题时非常关键。在ACM(国际大学生程序设计竞赛)中,最小生成树算法是常见的一种解决问题的策略。本篇文章将详细探讨最小生成树及其在ACM竞赛中的应用。 ...
根据给定文件的信息,本文将深入探讨两种构造最小生成树的经典算法——普里姆(Prim)算法与克鲁斯卡尔(Kruskal)算法,并通过具体的代码实现来展示这两种算法的应用场景与实现细节。 ### 一、最小生成树概念 #### ...
### 数据结构——图的最小生成树(邻接矩阵、普利姆) #### 概述 在计算机科学领域,图是一种非常重要的数据结构,用于表示实体之间的关系。在图论中,一个图由顶点集(节点)和边集组成,其中边连接两个顶点来...
### 使用Prim和Kruskal算法构建最小生成树 在图论中,最小生成树(Minimum Spanning Tree, MST)是一个非常重要的概念。对于一个连通的无向图来说,如果存在一个子图,它包含原图的所有顶点,并且这些顶点通过边...
总结来说,最小生成树是解决网络优化问题的关键工具,普里姆算法和克鲁斯卡尔算法则是构建最小生成树的常用方法,各有其适用场景和优缺点。在实际问题中,理解并灵活运用这些算法有助于找到最优解。
图的最小生成树PRIM算法课程设计 本课程设计的主要目的是实现图的最小生成树,使用普里姆算法来实现。普里姆算法是一种常用的图算法,用于寻找无向图中的最小生成树。该算法通过逐个遍历图中的顶点,记录权值最小的...
总结,MFC提供了丰富的图形界面工具,结合最小生成树的理论,我们可以开发出直观易用的可视化软件,帮助学习者更好地理解和掌握这类算法。在实现过程中,不仅需要对MFC框架有深入理解,还需要掌握图论、数据结构和...
### 度约束为2的最小生成树算法 #### 背景与定义 最小生成树(Minimum Spanning Tree, MST)是图论中的一个经典问题,主要研究如何在一个加权无向图中找到一棵包含所有顶点且总权重最小的生成树。在实际应用中,...
### 最小生成树C++代码实现解析 #### 一、最小生成树概念 最小生成树(Minimum Spanning Tree,简称MST)是指在一个加权无向图中找到一棵包含所有顶点的生成树,使得其边的权重之和最小。在实际应用中,最小生成树...
最小生成树是图论中的经典问题,也是一个重要部分,一般书上往往只介绍求最小生成树的算法,而忽略了更精彩的算法应用部分。本文将对最小生成树算法及其应用作全面的分析说明,使大家对此有更加深刻的认识。本文分三...
### 构造可以使n个城市连接的最小生成树 #### 综合训练目的与要求 本次实习旨在通过实际项目加深学生对数据结构与C程序设计的理解与应用能力。具体目标包括: 1. **理论联系实际**:让学生通过具体的编程任务,如...
总结起来,"最小生成树MFC"项目是一个结合了图论算法和MFC图形界面编程的应用,它可以帮助用户直观理解和操作最小生成树的构建过程。通过使用此工具,无论是学习算法还是解决实际问题,都能提供极大的便利。在实际...
《算法分析与设计-实验一 最小生成树实验报告》 最小生成树是图论中的一个重要概念,它在计算机科学中有着广泛的应用,特别是在网络设计、最优化问题和数据结构中。本次实验主要关注的是如何使用Kruskal算法来找到...
数据结构课程设计报告——最小生成树 最小生成树是图论中的一个重要概念,它涉及到如何在给定的连通图中找到一棵边的集合,使得这些边连接了图中的所有顶点,且总权重最小。这个任务在各种工程优化问题中都有应用,...
最小生成树是图论中的一个重要概念,用于寻找一个无向加权图的边集合,使得这些边连接了图中的所有顶点,同时整个边集合的总权重尽可能小。在这个问题中,Prim算法和Kruskal算法是两种常用的方法。 1. Prim算法: ...
根据给定的信息,我们可以分析并总结出以下与“最小生成树”相关的知识点: ### 数据结构基础知识 1. **数据类型定义**: - `edge` 结构体:用于表示图中的边,包含三个整型成员变量,分别表示边的起点(`begin`...
总结来说,Prim算法和Kruskal算法都是求解最小生成树的有效方法,各有优缺点。Prim算法更适用于处理稠密图,因为它的时间复杂度与图中顶点的数量成正比;而Kruskal算法则在处理稀疏图时效率更高,因为它的主要时间...
### 最小生成树(MST)及其MATLAB实现 #### 一、最小生成树的基本概念 在图论中,最小生成树是指在一个加权无向图中寻找一棵包含所有顶点的生成树,使得该树上所有边的权重之和最小。这种结构在计算机科学和网络设计...
总结来说,最小生成树算法是图论中的重要概念,Prim和Kruskal是其常见解法,而数据结构如邻接矩阵、邻接表、二叉堆和并查集则为实现这些算法提供了支持。这个压缩包的内容将帮助你理解和掌握这些知识,进一步拓展你...