不相交集合的数据结构
本来想着来实现基于贪婪思想的Kruskal算法—–最小生成树的算法之一。
却发现当我们合并集合时里面还涉及到一个判断“环”的问题,继而有了本篇博文:不相交集合的数据结构。
关于不相交集合的数据结构,这里作一个简单的介绍,更多的可看这里
- 第一:我们假设我们有n个不相交的集合{Si},i=1~n;其中每个集合中都有一个“代表元素”(这个“代表元素”你可以理解为我们班级中的“班长”,对外“班长”就代表了我们整个班级);
- 第二:不相交的集合的数据结构,要支持UNION(x,y)操作:将包含x和y的两个动态集合(Si和Sj)合并成一个新的集合,即完成两个集合的合并,然后为这个新的集合选取一个“代表元素”,虽然UNION的很多实现都是直接选取Si或者是Sj的“代表元素”继续作为合并后集合的“代表元素”,但是你也可以选择集合中的其他元素作为代表元素。
- 第三 、不相交的集合的数据结构,支持FIND-SET(x)操作:返回x所在集合的代表元素。
不相交集合数据结构有一些应用,例如:确定无向图的连通分量,以及图中是否有环。
下面我们用java来实现判断一个图中是否有环,判断是否有环的思想可以看这里
第一步:我们定义了一个边的类,如下
package org.wrh.algorithmdemo;
//边的类
public class Edge {
/*
* 边的始点
* */
private int src;
/*
* 边的终点
* */
private int dest;
public Edge(int src, int dest) {
super();
this.src = src;
this.dest = dest;
}
public int getSrc() {
return src;
}
public void setSrc(int src) {
this.src = src;
}
public int getDest() {
return dest;
}
public void setDest(int dest) {
this.dest = dest;
}
}
第二步:我们定义的一个图的类,如下
package org.wrh.algorithmdemo;
import java.util.List;
//图的类
public class Graph {
/*
* 图中的顶点的个数
* */
private int vertices_number;
/*
* 图中的边的个数
* */
private int edges_number;
/*
* 图中边对象的引用集合
* */
private List<Edge> edge;
//下面为构造函数和属性的get、set方法
public Graph(int vertices_number, int edges_number) {
super();
this.vertices_number = vertices_number;
this.edges_number = edges_number;
}
public int getVertices_number() {
return vertices_number;
}
public void setVertices_number(int vertices_number) {
this.vertices_number = vertices_number;
}
public int getEdges_number() {
return edges_number;
}
public void setEdges_number(int edges_number) {
this.edges_number = edges_number;
}
public List<Edge> getEdge() {
return edge;
}
public void setEdge(List<Edge> edge) {
this.edge = edge;
}
}
第三步:主函数类,如下
package org.wrh.algorithmdemo;
import java.util.ArrayList;
import java.util.List;
public class DisjuntSetCircle {
public static void main(String[] args) {
/*
* 给定边的数量和顶点的数量
* */
int vertices_num=4;
int edges_num=4;
/*
* new一个Graph对象,
* */
Graph graph=new Graph(vertices_num,edges_num);
/*
* 新建edges_num个Edge对象,构造一个List对象
* */
List<Edge> edge=new ArrayList<Edge>();
edge.add(new Edge(0,1));
edge.add(new Edge(1,2));
edge.add(new Edge(2,3));
edge.add(new Edge(3,0));
/*
* 将边加载到图中
* */
graph.setEdge(edge);//这样就构成了一个4个顶点4条边的图
/*
*定义parent数组来记录每个顶点属于那个集合的"代表元素";
* 例如:我们的学生管理系统一般会记录我们的"班长"是谁一样
*
* */
int parent []=new int[vertices_num];
/*
* 首先我们将这些集合的代表元素初始化为 -1,表示他们都是单个元素的集合
* */
for(int i=0;i<parent.length;i++){
parent[i]=-1;
}
/*
* 下面来判断这个图中是否有环
* */
if(isCycle(graph,parent)){
System.out.println("此图有环");
}
else{
System.out.println("此图没有环");
}
}
private static boolean isCycle(Graph graph,int[] parent) {
/*
*获取边的数量
*/
int num=graph.getEdge().size();
int src_represent;//用来表示边的起始点的"代表元素"
int dest_represent;//用来表示边的终点的"代表元素"
for(int i=0;i<num;i++){
int src=graph.getEdge().get(i).getSrc();//得到边的起始点
int dest=graph.getEdge().get(i).getDest();//得到边的终点
src_represent=find(parent,src);//得到起点所属集合的代表元素
dest_represent=find(parent,dest);//同上
if(src_represent==dest_represent){//说明,边的两个顶点已经出现在了集合中,加上此边之后,构成"环"
return true;
}
else{//否则,合并
union(parent,src_represent,dest_represent);
}
}
return false;
}
/*
* 合并两个不相交的集合
* */
private static void union(int[] parent, int src, int dest) {
/*
* 由于两者是两个集合的不同的"代表元素",因此将其中的的“代表元素”改为另外一个即可完成合并
* */
parent[src]=dest;
}
/*
* 用来寻找顶点X所在集合的"代表元素"
* */
private static int find(int[] parent, int x) {
/*
* 首先判断顶点x的"代表元素是不是等于-1",若等于-1,则说明,其自身就是"代表元素";
* 若不等于-1,则说明此点在某个集合中并且不是代表元素,我们需找到他的代表元素的标号,即我们需要向上查找
* */
if(parent[x]==-1){
return x;
}
return find(parent,parent[x]);
}
}
上面的代码中注释写的比较详细,这里就不在解释,相信大家都能够看懂
总结
- 这篇博文是关于“环”的检测的思想还是挺简单的,主要是为我们后面最小生成树算法的java实现做准备的,关于最小生成树的java实现,我最近将会完成。
关于”环”的检测的C语言实现和不相交集合的数据结构的知识可看这里,在此,对geeksforgeeks表示感谢,在这里,我学习到了很多知识。
时间: 2024-10-25 14:16:51