Kruskal算法求最小生成树
测试数据:
5 6
0 1 5
0 2 3
1 2 4
2 4 2
2 3 1
1 4 1
输出:
2 3 1
1 4 1
2 4 2
0 2 3
思路:在保证不产生回路的情况下,选择权值小的边。是否产生回路采用并查集来实现
判断两个点是否连通:如果两个点的祖先不是同一个,说明这两个点不可联通,要标志两个点联通,只要使两个点的祖先是同一个。关于并查集的讲解可以看我转载的一篇文章。
比如初始化的时候ABC的祖先是他自己,先加入了AB这条边,这两个点的的父亲不一样,可以加入这条边,同时将A的祖先设置为B的祖先。同样AC加入也是这样,将C的父亲设为A的祖先B。这时候发现ABC相互可达,他们的祖先都是一个B。这个时候如果要添加BC这条边,查看BC的祖先,发现祖先一样,说明这两个点是可达的,那么如果添加这条边就会产生回路。
总的来说,将相邻的边所有顶点的祖先设置为同一个,如果两个顶点的祖先是同一个,那么就表明这两个点是可达的。那么如果还要添加这个以这两个顶点为顶点的边,就会产生回路。
import java.io.BufferedReader;
import java.io.FileReader;
public class Kruskal {
static int father[];
static Edge edges[];
static Edge tree[];
static int verNum,edgeNum;
public static void main(String[] args) throws Exception {
readData();
initFather();
getTree();
}
/**
* 读取文本的数据
* @throws Exception
*/
private static void readData()throws Exception {
// TODO 自动生成的方法存根
BufferedReader bf=new BufferedReader(new FileReader("d:/testGraphic.txt"));
String strings[]=bf.readLine().split(" ");
verNum=Integer.parseInt(strings[0]);
edgeNum=Integer.parseInt(strings[1]);
edges=new Edge[edgeNum];
tree=new Edge[verNum-1];
for (int i = 0; i < edgeNum; i++) {
strings=bf.readLine().split(" ");
edges[i]=new Edge(Integer.parseInt(strings[0]),Integer.parseInt(strings[1]),Integer.parseInt(strings[2]));
}
sort();
bf.close();
}
/**
* 将各条边按照权值从小到大进行排列
*/
public static void sort()
{
for (int i = 0; i < edges.length; i++) {
for (int j = i+1; j < edges.length; j++) {
if(edges[j].weight<edges[i].weight)
{
Edge temp=edges[j];
edges[j]=edges[i];
edges[i]=temp;
}
}
}
}
/**
* 初始化每个点的父亲设置为自己
*/
private static void initFather() {
// TODO 自动生成的方法存根
father=new int[verNum];
for (int i = 0; i < father.length; i++) {
father[i]=i;
}
}
/**
* 获得最小生成树
*/
private static void getTree() {
// TODO 自动生成的方法存根
int edgesIndex=0;
int treeIndex=0;
while(true)
{
//如果边数够了跳出循环
if(treeIndex==verNum-1)
break;
int father1=findFather(edges[edgesIndex].from);
int father2=findFather(edges[edgesIndex].end);
//如果两个的点的父亲不相同 则说明不连通
if(father1!=father2)
{
tree[treeIndex++]=edges[edgesIndex];
father[father1]=father2;
}
edgesIndex++;
}
//输出树
printTree();
}
private static void printTree() {
// TODO 自动生成的方法存根
for (int i = 0; i < tree.length; i++) {
Edge edge=tree[i];
System.out.println(edge);
}
}
/**
* 查找一个点的父亲
* @param i
* @return
*/
public static int findFather(int i)
{
//如果一个点的父亲是自己 返回
if(father[i]==i)
return i;
//否则一直查找他的顶层。
else
{
int f=i;
while(father[f]!=f)
{
f=father[f];
}
father[i]=father[f];
return father[f];
}
}
}
class Edge
{
public int from;
public int end;
public int weight;
public Edge(int from,int end,int weight)
{
this.from=from;
this.end=end;
this.weight=weight;
}
public String toString()
{
return this.from+" "+this.end+" "+this.weight;
}
}
prim算法求最小生成树
思路:先随机找一个点添加到点集U中,在V-U中找一个点与U点集中关联并且权值最小的点,然后将这个点添加到点集U中,依次下去,直至U==V
测试数据:
5 6
0 2 3
1 2 4
2 4 2
2 3 1
1 4 1
输出:
0 2 3
2 3 1
2 4 2
4 1 1
import java.io.BufferedReader;
import java.io.FileReader;
public class Prime {
static int array[][];
static int verNum,edgeNum;
/**
* 读取文本数据
* @throws Exception
*/
public static void readData() throws Exception
{
BufferedReader bf=new BufferedReader(new FileReader("d:/testGraphic.txt"));
String strings[]=bf.readLine().split(" ");
verNum=Integer.parseInt(strings[0]);
edgeNum=Integer.parseInt(strings[1]);
array=new int [verNum][verNum];
//用矩阵表示两个点之间的权值,如果是相同点标志为0 如果两个点不关联设为100
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array.length; j++) {
if(i==j)
array[i][j]=0;
else
array[i][j]=100;
}
}
//根据文本进行赋值
for (int i = 0; i < edgeNum; i++) {
strings=bf.readLine().split(" ");
int a=Integer.parseInt(strings[0]);
int b=Integer.parseInt(strings[1]);
int c=Integer.parseInt(strings[2]);
array[a][b]=c;
array[b][a]=c;
}
}
/**
* 打印生成最小树
* @param tree
*/
public static void printTree(int tree[][])
{
for(int a[]:tree)
{
for(int b:a)
{
System.out.print(b+" ");
}
System.out.println();
}
}
public static void main(String[] args) throws Exception
{
readData();
//创建最小生成树
int tree[][]=new int [verNum-1][3];
//取一个点放到点集U
int k=0;
CloseEdge edge[]=new CloseEdge[verNum];
//其他点和点K的权值
for (int i = 0; i < verNum; i++) {
edge[i]=new CloseEdge(k,array[k][i]);
}
edge[k]=new CloseEdge(k,0);
//执行次数为边的数量
for (int o = 0; o< verNum-1; o++)
{
int min=Integer.MAX_VALUE;
//在V-U中的点中找一个和U中点关联的并且权值最小的加入到U
for (int i = 0; i <verNum; i++) {
if(edge[i].weight!=0&&edge[i].weight<min)
{
min=edge[i].weight;
k=i;
}
}
//将新选取点的权值存到数中
tree[o][2]=edge[k].weight;
//设置选中的点到U的权值为0,表示已经在U中
edge[k].weight=0;
//将新增加的变记录下来,第0个点是与新顶点相关联的的点,第一个顶点是新的顶点
tree[o][0]=edge[k].index;
tree[o][1]=k;
//因为有新顶点加入,所以重新设置V-U与点集U中点关联的最小的权值
for (int i = 0; i < edge.length; i++) {
if(array[k][i]<edge[i].weight)
edge[i]=new CloseEdge(k,array[k][i]);
}
}
printTree(tree);
}
}
class CloseEdge
{
int index;
int weight;
/**
*
* @param index
* @param weight
*/
public CloseEdge(int index,int weight)
{
this.index=index;
this.weight=weight;
}
}