图
图在生活被广泛应用,你平常听到的深度优先和广度优先算法都要建立图的基础之上.
图的相关概念很简单,这里先不给出复述,等到小编讲述到例子,一下子就豁然开朗了.
分析
我们代码中如何存储一个图呢?其实有常用的两种方式
一种是使用领接矩阵,也就是一个二维数组,数组下标是图点的表示,a[i][j]=1就表示i点和j点连通,若是为0表示不连通.
另一种是邻接表,也就是用一个一维数组表示这些图点,然后每个位置又链接出一个队列或者链表,该链存储的是与该点直接连通的点.(是直接连通,而不是间接连通,比如a-b-c,a和b是直接连通)
代码
package com.hyb.ds.图;
import sun.misc.Queue;
public class Graph {
//点
private final int v;
//边
private int e;
private final Queue<Integer>[] queues;
public Graph(int v){
this.v=v;
this.e=0;
queues= new Queue[v];
for (int i = 0; i < v; i++) {
queues[i]= new Queue<Integer>();
}
}
public int getV(){
return v;
}
public int getE(){
return e;
}
public void addE(int w,int m){
queues[w].enqueue(m);
queues[m].enqueue(w);
e++;
}
public Queue<Integer> getE(int i){
return queues[i];
}
}
class Test{
public static void main(String[] args) {
Graph graph = new Graph(13);
graph.addE(0,1);
graph.addE(0,2);
graph.addE(0,6);
graph.addE(0,5);
graph.addE(5,3);
graph.addE(5,4);
graph.addE(3,4);
graph.addE(4,6);
graph.addE(7,8);
graph.addE(9,10);
graph.addE(9,11);
graph.addE(9,12);
graph.addE(11,12);
DFS dfs = new DFS(graph, 0);
System.out.println(dfs.getCount());
System.out.println(dfs.marked(7));
System.out.println(dfs.marked(6));
System.out.println(dfs.marked(3));
}
}
深度优先搜索
请看上面的无向图中,深度优先搜索主要是为了判断与0点相通的点,从0开始出发,先往6走,走过的路标记为已走过,只要6没有被自己走过,就去往6,然后6继续以这样的规则向下搜索,如果发现四周字节相连的点都走过了,就原路返回,即如果搜索到了5这个点,发现0,3,4都搜索过了,就返回原来的点,比如3,来到3发现四周再搜索过了,就继续返回到4,一直返回不断探索四周是否有未被搜索的点,如果存在就搜索.一直到最起点开始都没有可搜索的点了,就结束.
package com.hyb.ds.图;
import com.hyb.ds.队列.ArrayQueue;
import sun.misc.Queue;
import java.util.Enumeration;
public class DFS {
private boolean[] marked;
//初始化跟顶点相通的数量
private int count;
public DFS(Graph graph,int first){
this.marked=new boolean[graph.getV()];
this.count=0;
dfs(graph,first);
}
public void dfs(Graph graph,int first){
marked[first]=true;
Queue<Integer> e = graph.getE(first);
Enumeration<Integer> elements = e.elements();
while (elements.hasMoreElements()){
Integer integer = elements.nextElement();
if (!marked(integer)){
dfs(graph,integer);
}
}
count++;
}
//判断某点是否与顶点相通
public boolean marked(int w){
return marked[w];
}
public int getCount(){
return count;
}
}
从上面的代码可以看出,当我们使用邻接表存储图的时候,深度优先搜索的路径是,先在对应点引出的队列里搜索,如果发现一个点未被搜索,里面转为该点的队列开始搜索,而不在原队列开始搜索.
在这个图中,如果需要进行路径查找,也就是输出所有到达的节点的路径,直接再造一个数组即可.
广度优先搜索
广度优秀搜索与深度优先相反,深度优先追求,往深的地方找,也就是该点能连通,就往该点的子节点里去找.不断深入子节点的子节点去找,直到发现有一个子节点附近所有的节点都搜索过了,就原路折返继续这样的规则.
而广度优先搜索的话,是先找附近所有能连通的节点,再去往某个连通的节点里去找附近能连通的它的子节点.
也就是从上面的图里我们可以看到,如果采用了广度优先搜索:
0->5->1->2->6 5->3->4
代码实现思路:
我们可以使用一个辅助队列去实现,将当前点入队,然后当队列不为空,就弹出队列元素,第一次弹出的是当前点,所以根据这个当前点找到延伸的邻接表,不断搜索,不断入队,之后循环队列的时候就可以将刚才搜索过点,按照先进先出的原则,去寻找对应的邻接表里搜索.
这和深度优先是不一样的,你可以想象一下,深度优先是在邻接表里找到一个点后,立马去递归该点的邻接表继续寻找.而这个广度优先是先搜索某个点的邻接表里所有的点入队,然后按照先进先出的原则去搜索各个点的邻接表.
import sun.misc.Queue;
import java.util.Enumeration;
public class BFS {
private boolean[] marked;
private int count;
//辅助队列
private Queue<Integer> queues;
public BFS(Graph graph,int first) throws InterruptedException {
marked=new boolean[graph.getV()];
this.count=0;
queues=new Queue<Integer>();
bfs(graph,first);
}
public void bfs(Graph graph,int first) throws InterruptedException {
marked[first]=true;
queues.enqueue(first);
while (!queues.isEmpty()){
Integer dequeue = queues.dequeue();
Queue<Integer> e = graph.getE(dequeue);
Enumeration<Integer> elements = e.elements();
while (elements.hasMoreElements()){
Integer element = elements.nextElement();
if (!marked[element]){
queues.enqueue(element);
marked[element]=true;
}
}
System.out.println("搜索点->"+dequeue);
count++;
}
}
//判断某点是否与顶点相通
public boolean marked(int w){
return marked[w];
}
public int getCount(){
return count;
}
}
class Test1{
public static void main(String[] args) throws InterruptedException {
Graph graph = new Graph(13);
graph.addE(0,1);
graph.addE(0,2);
graph.addE(0,6);
graph.addE(0,5);
graph.addE(5,3);
graph.addE(5,4);
graph.addE(3,4);
graph.addE(4,6);
graph.addE(7,8);
graph.addE(9,10);
graph.addE(9,11);
graph.addE(9,12);
graph.addE(11,12);
BFS dfs = new BFS(graph, 0);
System.out.println(dfs.getCount());
System.out.println(dfs.marked(7));
System.out.println(dfs.marked(6));
System.out.println(dfs.marked(3));
}
}
畅通工程
和前面的题目一样,不过这个时候需求是判断两个城市是否连通
Graph graph = new Graph(20);
graph.addE(0,1);
graph.addE(6,9);
graph.addE(3,8);
graph.addE(5,11);
graph.addE(2,12);
graph.addE(6,10);
graph.addE(4,8);
BFS dfs = new BFS(graph, 9);
System.out.println(dfs.getCount());
System.out.println(dfs.marked(8));
System.out.println(dfs.marked(10));
有向图
上面讲解的都是无向图,也就是任意两点无论从哪一点触发都能连通,而有向图的话只能从起点连通到终点.
我们可以用上面的代码做一个简单的修改,增加一个布尔字段,如果有传入布尔值为true,则说明要创建有向图,当插入一条边的时候,就连接一条边就可以了.
同时我们可以增加一个有趣的函数:获取该图的反转图:
private Graph reverseGraph(){
Graph graph = new Graph(v);
for (int i = 0; i < v; i++) {
Queue<Integer> e = getE(i);
Enumeration<Integer> elements = e.elements();
while (elements.hasMoreElements()){
Integer integer = elements.nextElement();
addE(integer,i);
}
}
return graph;
}
拓扑排序
对一个有向图,设G=(V,E),V表示的是顶点集合,E表示的是顶点间的边关系,若 Vi -> Vj存在路径,则Vi一定排在Vj之前,则我们称这样的顶点序列为拓扑序列,使之构成拓扑序列的过程叫做拓扑排序。
从上面的定义可以看出,拓扑排序能进行的前提有两点:一是有向图,二是不能弧线循环图
如上图,排序后是下图所示
构成的拓扑序列有1->3->4->5 0->3->4->5 0->2->4->5
检查环路
无论你用那种方式进行拓扑,首先都得检查是否有环路,必须要在没有环路的前提下进行拓扑
- 我们可以利用深度优先算法,直接让所有的节点都走一遍.
- 这里需要设计两个标记位,一个是大循环的标记位,何为大循环?也就是以所有节点为起点的深度优先搜索,而对应还有一个小循环标记位,该小循环标记位记录的是从起点到终点走过的路.这个小循环在回溯的时候是要撤回的.
import sun.misc.Queue;
import java.util.Enumeration;
public class DirectedCycle {
//标记路是否走过(大循环)
private boolean[] marked;
//标记是否有环路
private boolean hasCycle;
//辅助数组,下标对应节点,值对应是否被访问过(监测环的小循环)
private boolean[] directed;
public DirectedCycle(Graph graph){
int v = graph.getV();
this.marked=new boolean[v];
this.directed=new boolean[v];
for (int i = 0; i < v; i++) {
if (!marked[i]){
dfs(graph,i);
}
}
}
private void dfs(Graph graph,int first){
marked[first]=true;
directed[first]=true;
Queue<Integer> e = graph.getE(first);
Enumeration<Integer> elements = e.elements();
while (elements.hasMoreElements()){
Integer integer = elements.nextElement();
if (!marked[integer]){
dfs(graph,integer);
}
//如果在小循环里发现,该点已经被访问过了,说明存在一个环
if (directed[integer]){
hasCycle=true;
return;
}
}
directed[first]=false;
}
public boolean isHasCycle(){
return hasCycle;
}
}
点入栈
如果该图没有环路,说明可拓扑,就是寻找可达序列,那么在这里最核心的实现在于点入栈.
也是用深度优先搜索的方式,搜索到最深点,然后在回溯的时候让点入栈,这样就得到一个连通的链路
package com.hyb.ds.图;
import com.hyb.ds.二叉树.PageTree;
import com.hyb.ds.设计模式.代理模式.动态代理.TargetObj;
import sun.misc.Queue;
import java.util.Enumeration;
import java.util.List;
import java.util.Stack;
public class TopSort {
//是否被访问过
private boolean[] marked;
//递归完毕后将节点反向存入栈中
private Stack<Integer> stack;
public TopSort(Graph graph){
int v = graph.getV();
marked=new boolean[v];
stack=new Stack<>();
//因为是有向图,所有要循环所有顶点
for (int i = 0; i < 2; i++) {
if (!marked[i]){
dfs(graph,i);
}
}
}
private void dfs(Graph graph,int first){
marked[first]=true;
//深度优先完毕后让顶点入栈
System.out.println("入栈点->"+first);
Queue<Integer> e = graph.getE(first);
Enumeration<Integer> elements = e.elements();
while (elements.hasMoreElements()){
flag=false;
Integer integer = elements.nextElement();
if (!marked[integer]){
dfs(graph,integer);
stack.push(first);
}
public Stack<Integer> getStack(){
return stack;
}
public List<Stack<Integer>> list(){
return list;
}
}
实现拓扑
实现拓扑就是上面两步相加起来
package com.hyb.ds.图;
import java.util.Stack;
public class TopoSort {
private Stack<Integer> stack;
public TopoSort(Graph graph){
DirectedCycle directedCycle = new DirectedCycle(graph);
boolean hasCycle = directedCycle.isHasCycle();
if (!hasCycle){
TopSort topSort = new TopSort(graph);
stack=topSort.getStack();
}
}
public boolean isCycle(){
return stack==null;
}
public Stack<Integer> getStack(){
return stack;
}
}
class Test4{
public static void main(String[] args) {
Graph graph = new Graph(6, true);
graph.addE(0,2);
graph.addE(0,3);
graph.addE(1,3);
graph.addE(2,4);
graph.addE(3,4);
graph.addE(4,5);
TopoSort topoSort = new TopoSort(graph);
Stack<Integer> stack = topoSort.getStack();
for (Integer i :
stack) {
System.out.println(i);
}
}
}
得到的结果是543201,但这样的结果小编觉得其实是不怎么友好的,因为得到这个序列只能说明这里某些点之间的互相连通,其实并不能从中不借助外力得到一个连通的序列的,比如在这里的0和1其实是不连通,大家都是起点,都可以连通到3而已.
升级拓扑
可以对所有能单向连通的序列输出,这些序列我们用一个list结合去存储.
在得到这些序列的关键在于,也是让所有的点进行深度优先搜索,只不过每一个节点实际上都参与了深度优先搜索,即在回溯的时候存放节点的栈要出栈,mark标记位也要返回原来的状态,让新的一次深度优先搜索不被上一次的影响.
我们在求前面的拓扑序列中,会将所有走过的点都记录下来了,最后返回到一个栈中,这其实是有些不明确的,因为我们不知道到底那两个点相通,加入这两个点同时是入度为0的起点怎么办? 所以你真实要得到所有连通的序列,只能借助手段让所有的点都进行深度优先,然后记录每次深度优先的节点路程.
当然,这也是建立在没有循环连接的情况下的.
package com.hyb.ds.图;
import com.hyb.ds.二叉树.PageTree;
import com.hyb.ds.设计模式.代理模式.动态代理.TargetObj;
import sun.misc.Queue;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Stack;
public class TopSort {
//是否被访问过
private boolean[] marked;
//递归完毕后将节点反向存入栈中
private Stack<Integer> stack;
//存储可达路径
private List<Stack<Integer>> list;
//放置list被多加
private boolean flag;
public TopSort(Graph graph){
int v = graph.getV();
marked=new boolean[v];
stack=new Stack<>();
list=new ArrayList<>();
//因为是有向图,所有要循环所有顶点
for (int i = 0; i < v; i++) {
if (!marked[i]){
dfs(graph,i);
}
}
}
private void dfs(Graph graph,int first){
flag=false;
marked[first]=true;
//深度优先完毕后让顶点入栈
stack.push(first);
System.out.println("入栈点->"+first);
Queue<Integer> e = graph.getE(first);
Enumeration<Integer> elements = e.elements();
while (elements.hasMoreElements()){
Integer integer = elements.nextElement();
if (!marked[integer]){
dfs(graph,integer);
}
}
if (!flag && !stack.empty()){
Stack<Integer> s = new Stack<>();
for (Integer i :
stack) {
s.push(i);
}
list.add(s);
flag=true;
}
stack.pop();
marked[first]=false;
}
public Stack<Integer> getStack(){
return stack;
}
public List<Stack<Integer>> list(){
return list;
}
}
升级拓扑之后,你可以在Topo函数里也有list结合接一下就可以测试了.
最小生成树
在一个加权的图中,寻找一些路径,这些路径要求连接所有连通点,以达到路径和最小.
Prim算法
该算法时寻找最小生成树的经典算法,如上图所示,该算法的步骤是: 假如从0开始,寻找与其直接相邻的点集合,如4,7,2,6,然后在这些点集合中寻找一个与其路径最短的点,如7,然后让7纳入0的阵营,将7和0看成一个整体,比如说看成一个点,再次寻找四周直接相连的点集合,比如说除了原来的点集合外,还有5,1,这两个点与原来的4,7,2,6构成一个点集合.
在构建点集合有注意两点:
- 首先是从4,7,2,6这个点集合中寻找到与0相连最近的点7后,要将7从点集合里删除,因为7纳入0的阵营后,已经看成一个点了,就不需要看7和0之间了.
- 其次是当7纳入0之后,再看四周的直接相连的点,是存在4的,但是这个4已经与0相连了,也就是说在你从0寻找的时候,4已经纳入点集合里了,当你从7-0开始看的时候,就不能直接将4纳入点集合了,而是要判断7到4的路径和4到0的路径哪个小,如果是前者较小,就覆盖原值,如果前者较大,就不用覆盖.
代码思路:
- 既然是要遍历到每个连通点,那么就要求有一个循环,这个可以是个队列,因为队列比较方便一点,我们先将0入队,然后出队,用先出队的这个点寻找点集合,寻找到之后,再讲最短路径上的点纳入进来,这个纳入进来的操作就是将7放入队列中,这样子纳入的点就有了个先后顺序去寻找四周的点集合
- 点集合的存储在于,要有一个数组,这个数组的存储很有讲究,下标是当前点的对应点,值是这两个点之间的距离,比如0和7这两个点,放在点集合中就是下标为7,值为7和0之间的长度.
- 进行完一个的点集合收集后,就进入一个函数比较寻找该数组里的最小的点.
代码步骤
- 首先我们先构造一个对象,对象是点与点之间的边对象
public class EdgeW implements Comparable {
private int w;
private int v;
private double weight;
public EdgeW(int w, int v, double weight){
this.w=w;
this.v=v;
this.weight=weight;
}
public double getWeight() {
return weight;
}
public int getW() {
return w;
}
public int getV() {
return v;
}
@Override
public int compareTo(Object o) {
if (o instanceof EdgeW){
EdgeW e=(EdgeW) o;
return Double.compare(this.getWeight(), e.getWeight());
}
return -2;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof EdgeW){
EdgeW e=(EdgeW) obj;
return this.v == e.v && this.w == e.w && this.weight == e.weight;
}
return false;
}
@Override
public String toString() {
return "["+this.w+"<->"+this.v+" weight:"+weight+"]";
}
public int other(int i) {
if (i==v)
return w;
else
return v;
}
}
- 之后构造一个权重图:
package com.hyb.ds.图;
import sun.misc.Queue;
import java.util.Enumeration;
public class WeightGraph {
//顶点
private int v;
//边
private int e;
private Queue<EdgeW>[] edgeQueues;
private boolean type;
public WeightGraph(int v,boolean type) {
this.v = v;
this.edgeQueues = new Queue[v];
this.type = type;
for (int i = 0; i < v; i++) {
edgeQueues[i]=new Queue<EdgeW>();
}
}
public int getV(){
return v;
}
public int getW(){
return e;
}
public void addEdge(EdgeW edge){
int w = edge.getW();
int v = edge.getV();
edgeQueues[w].enqueue(edge);
if (!type){
edgeQueues[v].enqueue(edge);
}
e++;
}
//获取和顶点v关联的所有边
public Queue<EdgeW> getEdges(int v){
return edgeQueues[v];
}
//获取所有边
public Set<EdgeW> edges(){
HashSet<EdgeW> edgeWS = new HashSet<>();
for (int i = 0; i < this.v; i++) {
Queue<EdgeW> edges = getEdges(i);
Enumeration<EdgeW> elements =
edges.elements();
while (elements.hasMoreElements()){
EdgeW edgeW = elements.nextElement();
edgeWS.add(edgeW);
}
}
return edgeWS;
}
}
- 下面便是核心代码:
package com.hyb.ds.图;
import sun.misc.Queue;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
public class PrimMST {
//保存现有的最短路径
private List<EdgeW> edgeTo;
//索引代表顶点,判断该顶点是否已经在树中
private boolean[] marked;
//存放切分的有效横切边,索引是与该点的最近相连的点(不是当前点),值是这个边对象
private EdgeW[] pq;
public PrimMST(WeightGraph graph) throws InterruptedException {
int v = graph.getV();
edgeTo =new ArrayList<>();
marked=new boolean[v];
pq =new EdgeW[v];
visit(graph,0);
}
public void visit(WeightGraph graph,int v) throws InterruptedException {
//创建分割点的队列
Queue<Integer> queue=new Queue<>();
//第一次让第一个点进来
queue.enqueue(v);
//当这个队列不为空
while (!queue.isEmpty()){
//吐出队列中先进入的点
Integer dequeue = queue.dequeue();
//标记该点已经被访问过了
marked[dequeue]=true;
//获取该点所有直连的点的队列
Queue<EdgeW> edges = graph.getEdges(dequeue);
//遍历该队列,找到与其最小路径的点
Enumeration<EdgeW> elements = edges.elements();
while (elements.hasMoreElements()){
//找到某条边
EdgeW edgeW = elements.nextElement();
//获取当前点在这条边上对应的点
int other = edgeW.other(dequeue);
//如果这点已经在过去被当成dequue点了,就直接跳过
if (marked[other])
continue;
//如果这个点延伸出的边本来就在pq数组中
if (pq[other]==null){
//如果other延伸出的边不在数组中,就直接加入进入
pq[other]=edgeW;
}else {
//就比较当前other点延伸出的边比原来的是否小
if (edgeW.getWeight() < pq[other].getWeight()) {
//如果当前边的权重比本来存在的边的权重要小,就代替它
pq[other] = edgeW;
}
}
}
//根据得到的pq数组中找一条最小的边
int j = findMinW(pq);
//回到原点就不用再入队了
if (j!=0){
queue.enqueue(j);
}
}
}
private int findMinW(EdgeW[] pq) {
EdgeW x=new EdgeW(0,0,Double.MAX_VALUE);
int j=0;
for (int i = 1; i < pq.length; i++) {
if (pq[i]!=null){
if (pq[i].getWeight()<x.getWeight()){
x=pq[i];
j=i;
}
}
}
//防止回到原点的时候将list集合里的东西
// 代替了
if (j!=0){
int first = x.other(j);
edgeTo.add(new EdgeW(first,j,x.getWeight()));
//将最小边从数组记录里移除
pq[j]=null;
}
return j;
}
public List<EdgeW> getEdges(){
return edgeTo;
}
}
class Test5{
public static void main(String[] args) throws InterruptedException {
WeightGraph weightGraph = new WeightGraph(8, false);
EdgeW edgeW = new EdgeW(0, 2, 0.26);
EdgeW edgeW1 = new EdgeW(0, 7, 0.16);
EdgeW edgeW2 = new EdgeW(0, 4, 0.38);
EdgeW edgeW3 = new EdgeW(0, 6, 0.58);
EdgeW edgeW4 = new EdgeW(6, 2, 0.40);
EdgeW edgeW7 = new EdgeW(6, 3, 0.52);
EdgeW edgeW8 = new EdgeW(6, 4, 0.93);
EdgeW edgeW5 = new EdgeW(2, 7, 0.34);
EdgeW edgeW6 = new EdgeW(2, 3, 0.17);
EdgeW edgeW9 = new EdgeW(2, 1, 0.36);
EdgeW edgeW11 = new EdgeW(7, 4, 0.37);
EdgeW edgeW12 = new EdgeW(7, 5, 0.28);
EdgeW edgeW13 = new EdgeW(7, 1, 0.19);
EdgeW edgeW18 = new EdgeW(7, 2, 0.34);
EdgeW edgeW14 = new EdgeW(5, 1, 0.32);
EdgeW edgeW15 = new EdgeW(5, 4, 0.35);
EdgeW edgeW16 = new EdgeW(1, 3, 0.29);
weightGraph.addEdge(edgeW);
weightGraph.addEdge(edgeW7);
weightGraph.addEdge(edgeW8);
weightGraph.addEdge(edgeW9);
weightGraph.addEdge(edgeW11);
weightGraph.addEdge(edgeW12);
weightGraph.addEdge(edgeW13);
weightGraph.addEdge(edgeW18);
weightGraph.addEdge(edgeW14);
weightGraph.addEdge(edgeW15);
weightGraph.addEdge(edgeW16);
weightGraph.addEdge(edgeW1);
weightGraph.addEdge(edgeW2);
weightGraph.addEdge(edgeW3);
weightGraph.addEdge(edgeW4);
weightGraph.addEdge(edgeW5);
weightGraph.addEdge(edgeW6);
PrimMST primMST = new PrimMST(weightGraph);
List<EdgeW> edges = primMST.getEdges();
for (EdgeW e :
edges) {
System.out.println(e.toString());
}
}
}
说明: 小编的记录数据结构与算法的步骤是参考黑马和尚硅谷的.这个最小生成树是参照前者的,本来是看着视频照做的,但是却发现黑马的视频有很多弊端,这里的最小生成树就是例子,把简单的问题复杂化了,至于怎么复杂,可以看看黑马.
Kruskal算法
这么算法小编觉得比上一个算法更加简单,这个算法是从全局上看的,也就是不断遍历该图边的集合,然后寻找最小的那条边,直到这些边连通成一棵最小生成树.
如上图所示: 我们先遍历所有的边,寻找到一条最小的边,假如是3-2,那么就将这条边纳入Set集合中.再次遍历剩下的边,假如这次7-0,就将7-0纳入Set集合中.
这里的难点在于,假如我们得到了两条边3-2和2-6纳入了集合后,那遍历的时候如果发现3-6在剩余的边里是最小的怎么办?这种情况肯定是会出现的,但是我们不能将其纳入到Set中,因为这样子就不是最小生成树了.
其实这个问题利用并查集可以很巧妙的解决这个问题,比如我们在得到3-2和2-6这两条边并放入集合后,还可以将这两边放入到并查集中,也就说明3-2-6是连通的,这个时候如果发现3-6是剩余边中最小的,就可以先判断这两点是否连通,如果是非连通,就可以纳入Set中,如果是连通的,再纳入就构不成最小生成树了
package com.hyb.ds.图;
import com.hyb.ds.并查集.UF;
import java.util.*;
public class Kruskal {
private Set<EdgeW> set;
private UF uf;
public Kruskal(WeightGraph graph){
set=new HashSet<>();
Set<EdgeW> edges = graph.edges();
int size = edges.size();
int v = graph.getV();
uf=new UF(v);
for (int i = 0; i < size; i++) {
kruskal(edges);
}
}
private void kruskal(Set<EdgeW> edges) {
Iterator<EdgeW> iterator = edges.iterator();
EdgeW edgeW=new EdgeW(-1,-1,Double.MAX_VALUE);
while (iterator.hasNext()){
EdgeW w = iterator.next();
if (w.getWeight()<edgeW.getWeight()){
edgeW=w;
}
}
int w = edgeW.getW();
int v = edgeW.other(w);
if (!uf.connected(w,v)){
uf.unioned(w,v);
set.add(edgeW);
}
edges.remove(edgeW);
}
public Set<EdgeW> getSet(){
return set;
}
}
class Test6{
public static void main(String[] args) throws InterruptedException {
WeightGraph weightGraph = new WeightGraph(8, false);
EdgeW edgeW = new EdgeW(0, 2, 0.26);
EdgeW edgeW1 = new EdgeW(0, 7, 0.16);
EdgeW edgeW2 = new EdgeW(0, 4, 0.38);
EdgeW edgeW3 = new EdgeW(0, 6, 0.58);
EdgeW edgeW4 = new EdgeW(6, 2, 0.40);
EdgeW edgeW7 = new EdgeW(6, 3, 0.52);
EdgeW edgeW8 = new EdgeW(6, 4, 0.93);
EdgeW edgeW5 = new EdgeW(2, 7, 0.34);
EdgeW edgeW6 = new EdgeW(2, 3, 0.17);
EdgeW edgeW9 = new EdgeW(2, 1, 0.36);
EdgeW edgeW11 = new EdgeW(7, 4, 0.37);
EdgeW edgeW12 = new EdgeW(7, 5, 0.28);
EdgeW edgeW13 = new EdgeW(7, 1, 0.19);
EdgeW edgeW18 = new EdgeW(7, 2, 0.34);
EdgeW edgeW14 = new EdgeW(5, 1, 0.32);
EdgeW edgeW15 = new EdgeW(5, 4, 0.35);
EdgeW edgeW16 = new EdgeW(1, 3, 0.29);
weightGraph.addEdge(edgeW);
weightGraph.addEdge(edgeW7);
weightGraph.addEdge(edgeW8);
weightGraph.addEdge(edgeW9);
weightGraph.addEdge(edgeW11);
weightGraph.addEdge(edgeW12);
weightGraph.addEdge(edgeW13);
weightGraph.addEdge(edgeW18);
weightGraph.addEdge(edgeW14);
weightGraph.addEdge(edgeW15);
weightGraph.addEdge(edgeW16);
weightGraph.addEdge(edgeW1);
weightGraph.addEdge(edgeW2);
weightGraph.addEdge(edgeW3);
weightGraph.addEdge(edgeW4);
weightGraph.addEdge(edgeW5);
weightGraph.addEdge(edgeW6);
Kruskal kruskal = new Kruskal(weightGraph);
Set<EdgeW> set = kruskal.getSet();
for (EdgeW next : set) {
System.out.println(next.toString());
}
}
}