赞
踩
本博客是博主用于复习数据结构以及算法的博客,如果疏忽出现错误,还望各位指正。
Kruskal算法的原理:
就是每次取最小的边,看看是不是与已经选择的构成回路,不构成回路,就加进来,继续,直到选了N-1条边。
对于判断回路来说,BFS和DFS应该是都可以的(类似于判断连通分量),但是,之前已经写过BFS和DFS代码了,所以在这里用并查集实现(当然,博主自己没试过DFS和BFS,感兴趣的可以自己去试一试)。
还是这个图,底下的是使用排序排好序的EData类。
首先,我们可以把排序的代码单拎出来
- //用于对之前定义的to进行排序
- public void toSort(){
- Collections.sort(this.to, new Comparator<EData>() {
- @Override
- public int compare(EData o1, EData o2) {
- //顺序对就是升序,顺序不对就是降序,比如我现在是o1和o2传进来,比较时候o1在前就是升序,o1在后就是降序
- int result = Integer.compare(o1.weight,o2.weight);
- return result;
- }
- });
- }
之后因为要用到并查集,我们把并查集的基本代码拎出来
- //并查集查找
- public int find(int x,int[] f){
- while(x!=f[x]){
- x = f[x];
- }
- return x;
- }
- //并查集合并
- public int union(int x,int y,int[] f){
- find(x,f);
- find(y,f);
- if(x!=y){
- f[x] = y;
- return y;
- }
- return -1; //如果一样的集合就返回-1
- }
之后就来首先Kruskal,注意最后返回的是一个ArrayList,为了方便看选的哪条边,我就直接把两头都加进去了,有需要的话,自行更改代码即可
- public ArrayList<Integer> kruskal(){
- //kruskal是对form to weight这种结构的类进行排序,然后选取最短的一条边,看是否形成回路,加入
- toSort(); //调用toSort进行排序
- //由于要判断是否形成回路,我们在这用并查集
- //初始化并查集
- int[] f = new int[vertexList.size()];
- for(int i = 0;i<vertexList.size();i++){
- f[i] = i;
- }
- ArrayList<Integer> res = new ArrayList<>();
- int count = 0;
- for(int i = 0;count != vertexList.size()-1 && i<this.to.size();i++){
- //之后就是开始取边了
- EData temp = this.to.get(i);
- if(union(temp.start,temp.end,f)!=-1){//如果查到不是一个集,就可以添加
- //这里都加进来是方便看哪个边
- res.add(temp.start);
- res.add(temp.end);
- count++;
- }
- }
- return res; //最后把集合返回去就行
- }
以上就是Kruskal算法实现最小生成树,这个不需要指定点生成,会排序选择最小边,而Prim指定某个点开始生成。记住哈,最小生成树可能不唯一!
以下是完整的测试代码
- //package GraphTest.Demo;
-
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.LinkedList;
-
- public class Graph{//这是一个图类
- /***基础属性***/
- int[][] edges; //邻接矩阵存储边
- ArrayList<EData> to = new ArrayList<>(); //EData包含start,end,weight三个属性,相当于另一种存储方式,主要是为了实现kruskal算法定义的
- ArrayList<String> vertexList = new ArrayList<>(); //存储结点名称,当然你若想用Integer也可以,这个是我自己复习用的
- int numOfEdges; //边的个数
- boolean[] isVisited;
- //构造器
- Graph(int n){
- this.edges = new int[n][n];
- //为了方便,决定讲结点初始化为INF,这也方便后续某些操作
- int INF = Integer.MAX_VALUE;
- for(int i=0;i<n;i++){
- Arrays.fill(edges[i],INF);
- }
- this.numOfEdges = 0;
- this.isVisited = new boolean[n];
- }
- //插入点
- public void insertVertex(String vertex){//看自己个人喜好,我这边是一个一个在主方法里插入点的名称
- vertexList.add(vertex);
- }
- //点的个数
- public int getNumOfVertex(){
- return vertexList.size();
- }
- //获取第i个节点的名称
- public String getVertexByIndex(int i){
- return vertexList.get(i);
- }
- //获取该节点的下标
- public int getIndexOfVertex(String vertex){
- return vertexList.indexOf(vertex);
- }
- //插入边
- public void insertEdge(int v1,int v2,int weight){
- //注意,这里是无向图
- edges[v1][v2] = weight;
- edges[v2][v1] = weight;
- this.numOfEdges++;
- //如果要用Kruskal算法的话这里+
- to.add(new EData(v1,v2,weight)); //加入from to这种存储方式
- }
- //边的个数
- public int getNumOfEdge(){
- return this.numOfEdges;
- }
- //得到点到点的权值
- public int getWeight(int v1,int v2){//获取v1和v2边的权重
- return edges[v1][v2];
- }
- //打印图
- public void showGraph(){
- for(int[] line:edges){
- System.out.println(Arrays.toString(line));
- }
- }
- //获取index行 第一个邻接结点
- public int getFirstNeighbor(int index){
- for(int i = 0;i < vertexList.size();i++){
- if(edges[index][i] != Integer.MAX_VALUE){
- return i; //找到就返回邻接结点的坐标
- }
- }
- return -1; //没找到的话,返回-1
- }
- //获取row行 column列之后的第一个邻接结点
- public int getNextNeighbor(int row,int column){
- for(int i = column + 1;i < vertexList.size();i++){
- if(edges[row][i] != Integer.MAX_VALUE){
- return i; //找到就返回邻接结点的坐标
- }
- }
- return -1; //没找到的话,返回-1
- }
- //DFS实现,先定义一个isVisited布尔数组确认该点是否遍历过
-
- public void DFS(int index,boolean[] isVisited){
- System.out.print(getVertexByIndex(index)+" "); //打印当前结点
- isVisited[index] = true;
- //查找index的第一个邻接结点f
- int f = getFirstNeighbor(index);
- //
- while(f != -1){//说明有
- if(!isVisited[f]){//f没被访问过
- DFS(f,isVisited); //就进入该节点f进行遍历
- }
- //如果f已经被访问过,从当前 i 行的 f列 处往后找
- f = getNextNeighbor(index,f);
- }
- }
- //考虑到连通分量,需要对所有结点进行一次遍历,因为有Visited,所以不用考虑冲突情况
- public void DFS(){
- for(int i=0;i<vertexList.size();i++){
- if(!isVisited[i]){
- DFS(i,isVisited);
- }
- }
- }
-
- public void BFS(int index,boolean[] isVisited){
- //BFS是由队列实现的,所以我们先创建一个队列
- LinkedList<Integer> queue = new LinkedList<>();
- System.out.print(getVertexByIndex(index)+" "); //打印当前结点
- isVisited[index] =true; //遍历标志ture
- queue.addLast(index); //队尾加入元素
- int cur,neighbor; //队列头节点cur和邻接结点neighbor
- while(!queue.isEmpty()){//如果队列不为空的话,就一直进行下去
- //取出队列头结点下标
- cur = queue.removeFirst(); //可以用作出队
- //得到第一个邻接结点的下标
- neighbor = getFirstNeighbor(cur);
- //之后遍历下一个
- while(neighbor != -1){//邻接结点存在
- //是否访问过
- if(!isVisited[neighbor]){
- System.out.print(getVertexByIndex(neighbor)+" ");
- isVisited[neighbor] = true;
- queue.addLast(neighbor);
- }
- //在cur行找neighbor列之后的下一个邻接结点
- neighbor = getNextNeighbor(cur,neighbor);
- }
- }
- }
- //考虑到连通分量,需要对所有结点进行一次遍历,因为有Visited,所以不用考虑冲突情况
- public void BFS(){
- for(int i=0;i<vertexList.size();i++){
- if(!isVisited[i]){
- BFS(i,isVisited);
- }
- }
- }
-
- public void prim(int begin){
- //Prim原理:从当前集合选出权重最小的邻接结点加入集合,构成新的集合,重复步骤,直到N-1条边
- int N = vertexList.size();
- //当前的集合 与其他邻接结点的最小值
- int[] lowcost = edges[begin];
- //记录该结点是从哪个邻接结点过来的
- int[] adjvex = new int[N];
- Arrays.fill(adjvex,begin);
- //表示已经遍历过了,isVisited置true
- isVisited[begin] = true;
-
- for(int i =0;i<N-1;i++){//进行N-1次即可,因为只需要联通N-1条边
- //寻找当前集合最小权重邻接结点的操作
- int index = 0;
- int mincost = Integer.MAX_VALUE;
- for(int j = 0;j<N;j++){
- if(isVisited[j]) continue;
- if(lowcost[j] < mincost){//寻找当前松弛点
- mincost = lowcost[j];
- index = j;
- }
- }
- System.out.println("选择节点"+index+"权重为:"+mincost);
- isVisited[index] = true;
- System.out.println(index);
- //加入集合后更新的操作,看最小邻接结点是否更改
- for(int k = 0;k<N;k++){
- if(isVisited[k]) continue;//如果遍历过就跳过
- if(edges[index][k] < lowcost[k]){ //加入新的节点之后更新,检查原图的index节点,加入后,是否有更新的
- lowcost[k] = (edges[index][k]);
- adjvex[k] = index;
- }
- }
- }
- }
- //用于对之前定义的to进行排序
- public void toSort(){
- Collections.sort(this.to, new Comparator<EData>() {
- @Override
- public int compare(EData o1, EData o2) {
- //顺序对就是升序,顺序不对就是降序,比如我现在是o1和o2传进来,比较时候o1在前就是升序,o1在后就是降序
- int result = Integer.compare(o1.weight,o2.weight);
- return result;
- }
- });
- }
- //并查集查找
- public int find(int x,int[] f){
- while(x!=f[x]){
- x = f[x];
- }
- return x;
- }
- //并查集合并
- public int union(int x,int y,int[] f){
- find(x,f);
- find(y,f);
- if(x!=y){
- f[x] = y;
- return y;
- }
- return -1; //如果一样的集合就返回-1
- }
- public ArrayList<Integer> kruskal(){
- //kruskal是对form to weight这种结构的类进行排序,然后选取最短的一条边,看是否形成回路,加入
- toSort(); //调用toSort进行排序
- //由于要判断是否形成回路,我们可以用DFS或者BFS,因为之前都写过,所以我们在这用并查集
- //初始化并查集
- int[] f = new int[vertexList.size()];
- for(int i = 0;i<vertexList.size();i++){
- f[i] = i;
- }
- ArrayList<Integer> res = new ArrayList<>();
- int count = 0;
- for(int i = 0;count != vertexList.size()-1 && i<this.to.size();i++){
- //之后就是开始取边了
- EData temp = this.to.get(i);
- if(union(temp.start,temp.end,f)!=-1){//如果查到不是一个集,就可以添加
- //这里都加进来是方便看哪个边
- res.add(temp.start);
- res.add(temp.end);
- count++;
- }
- }
- return res; //最后把集合返回去就行
- }
-
-
- public static void main(String[] args) {
- int n = 5;
- String[] Vertexs ={"A","B","C","D","E"};
- //创建图对象
- Graph graph = new Graph(n);
- for(String value:Vertexs){
- graph.insertVertex(value);
- }
- graph.insertEdge(0,1,7);
- graph.insertEdge(0,2,1);
- graph.insertEdge(1,2,6);
- graph.insertEdge(1,3,3);
- graph.insertEdge(1,4,5);
- graph.insertEdge(3,4,8);
- graph.showGraph();
- graph.DFS(1, graph.isVisited);
- System.out.println();
- graph.DFS();//再求求所有的,看有没有剩下的
- System.out.println();
- Arrays.fill(graph.isVisited,false);
- graph.BFS(1, graph.isVisited);
- System.out.println();
- Arrays.fill(graph.isVisited,false);
- graph.BFS();
- System.out.println();
- Arrays.fill(graph.isVisited,false);
- graph.prim(2);
- graph.toSort();
- for(EData i : graph.to){
- System.out.println(i.toString());
- }
- System.out.println(graph.kruskal().toString());;
- }
- }
-
- class EData{
- //当然,这是为了方便,直接记录结点下标,而不记录像"A"这种
- int start;
- int end;
- int weight;
- EData(int start,int end,int weight){
- this.start = start;
- this.end = end;
- this.weight = weight;
- }
-
- @Override
- public String toString() {
- return "EData{" +
- "start=" + start +
- ", end=" + end +
- ", weight=" + weight +
- '}';
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。