Kruskal和Prim算法求最小生成树

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;
	}
}
posted @ 2017-04-25 16:10  jiajun_geek  阅读(1053)  评论(0编辑  收藏  举报