8

我正在尝试编写一个可以找到最小生成树的程序。但是我在使用这种算法时遇到的一个问题是测试电路。在java中执行此操作的最佳方法是什么。

好的,这是我的代码

import java.io.*;
import java.util.*;

public class JungleRoads 
{
    public static int FindMinimumCost(ArrayList graph,int size)
    {
        int total = 0;
        int [] marked = new int[size];      //keeps track over integer in the mst

        //convert an arraylist to an array
        List<String> wrapper = graph;
        String[] arrayGraph = wrapper.toArray(new String[wrapper.size()]);
        String[] temp = new String[size];
        HashMap visited = new HashMap();


        for(int i = 0; i < size; i++)
        {
           // System.out.println(arrayGraph[i]);
            temp = arrayGraph[i].split(" ");

            //loop over connections of a current node
            for(int j =  2; j < Integer.parseInt(temp[1])*2+2; j++)
            {

                if(temp[j].matches("[0-9]+"))
                {
                    System.out.println(temp[j]);
                }
            }


        }


        graph.clear();
        return total;


    }


    public static void main(String[] args) throws IOException
    {

         FileReader fin = new FileReader("jungle.in");
        BufferedReader infile = new BufferedReader(fin);

        FileWriter fout = new FileWriter("jungle.out");
        BufferedWriter outfile = new BufferedWriter(fout);


        String line;
        line = infile.readLine();
        ArrayList graph = new ArrayList();

        do
        {

            int num = Integer.parseInt(line);
            if(num!= 0)
            {

                int size = Integer.parseInt(line)-1;

                for(int i=0; i < size; i++)
                {
                    line = infile.readLine(); 
                    graph.add(line);
                }

               outfile.write(FindMinimumCost(graph, size));
            }   


            line = infile.readLine();
        }while(!line.equals("0"));

    }
}
4

5 回答 5

5

Kruskall 的算法不会搜索循环,因为它的性能效率不高;但是尝试创建一个树状的组件,然后将它们相互连接。如您所知,如果您将两棵不同的树与一条新边连接起来,您将创建新树并且无需检查循环。

如果您查看wiki 页面算法如下:

1. create a forest F (a set of trees), where each vertex in the graph is a separate tree
2. create a set S containing all the edges in the graph
3. while S is nonempty and F is not yet spanning
    a. remove an edge with minimum weight from S
    b. if that edge connects two different trees, then add it to the forest, combining 
       two trees into a single tree
    c. otherwise discard that edge.

您应该为此使用不相交集数据结构。再次来自维基:

首先在 O(E log E) 时间内使用比较排序按权重对边进行排序;这允许“从 S 中移除具有最小权重的边缘”的步骤以恒定时间运行。接下来,我们使用不相交集数据结构(Union&Find)来跟踪哪些顶点在哪些组件中。我们需要为每条边执行 O(E) 操作、两个“查找”操作和可能的一个联合。即使是简单的不相交集数据结构,例如按等级联合的不相交集森林,也可以在 O(E log V) 时间内执行 O(E) 操作。因此总时间为 O(E log E) = O(E log V)。


创建不相交的森林

现在你可以看看Boost Graph Library-Incremental Components部分。您应该实现一些方法:MakeSetFindUnion,然后您可以实现 kruskall 算法。您所做的只是使用一些集合,最简单的方法是使用链表。

每个集合都有一个元素作为代表元素,它是集合中的第一个元素。

1-首先通过链表实现MakeSet :

通过使图中的每个顶点成为其自己的组件(或集合)的成员,这为增量连接组件算法准备了不相交集数据结构。

您应该将每个顶点(元素)初始化为新集合的代表元素,您可以通过将它们设置为自己的父元素来做到这一点:

 function MakeSet(x)
   x.parent := x

2-实现查找方法:

找到包含顶点的集合的代表元素x

 function Find(x)
 if x.parent == x
    return x
 else
    return Find(x.parent)

if部分检查元素是否为代表元素。我们通过将集合的所有代表元素设置为它们自己的父元素,将它们设置为它们的第一个元素。

3-最后,当你得到所有以前的东西时,简单的部分是实现Union方法:

function Union(x, y)
 xRoot := Find(x) // find representative element of first element(set)
 yRoot := Find(y) // find representative element of second element(set)
 xRoot.parent := yRoot // set representative element of first set 
                       // as same as representative element of second set

现在你应该如何运行 Kruskall?

首先通过MakeSet方法将所有节点放入n不相交的集合中。在找到所需边后的每一步(未选中且最小的),通过Find方法找到其端点顶点的相关集合(它们的代表元素),如果它们相同,则将这条边丢弃,因为这条边会导致一个循环,但如果它们在不同的集合中,使用Union方法合并这些集合。因为每个集合都是树,它们的联合是树。

您可以通过为不相交集选择更好的数据结构来优化这一点,但现在我认为已经足够了。如果你对更高级的数据结构感兴趣,你可以实现rank base方式,你会在wiki中找到它,这很容易但我没有提到它以防止困惑。

于 2012-03-31T23:51:44.387 回答
2

如果顶点以某种方式被标记,您需要做的就是检查所选边的两个顶点是否已经被访问过,这将表明一个循环。

因此,如果它用整数实现,您可以使用布尔数组来标记已选择的顶点。

boolean[] visited = new boolean[vertex-count-1];

或者,如果顶点被标记为字符串,您可以将它们添加到 Map 例如并检查它们是否尚未添加。

于 2012-02-27T04:13:40.750 回答
2

要检查电路,您需要使用联合查找数据结构。最简单的方法就是使用链表,但有更有效的实现。如果您想自己实现一个,此链接可以为您提供帮助。

http://en.wikipedia.org/wiki/Disjoint-set_data_structure

或者这里是一个java实现的链接:

http://www.koders.com/java/fid125D835ECD1C1C701008C456A93A7179FA06D196.aspx

基本上,联合查找数据结构允许您跟踪不相交的元素集,并支持两种操作:

1) Find, which takes an element as an argument and tells you which set it is in
2) Union, which takes 2 disjoint sets and merges them

假设您将形成 MST 的边数组是S. 这个想法是,您可以C使用 Union-Find 创建一个集合 ,它跟踪图中的哪些顶点由 中的边连接S。当C包含图中的所有顶点时,您就完成了,检查添加一条边是否会创建一个循环相当于检查它连接的两个顶点是否已经存在C(通过使用两个Find操作)。

因此,如果E是图中所有边的集合,您的更新操作可以像这样进行:

    Remove edge, e from E, with minimum weight (say that it connects vertices v1 and v2)
    Find(v1) and Find(v2)
    If v1 and v2 are both in C then continue
    Else, Union(C,{v1,v2}) and append e to S

并且您停止更新一次C包含图形的每个顶点。

于 2012-03-31T23:53:22.550 回答
0

如果您检查循环,使用 DFS 将非常低效。但是您可以使用Disjoint Set。连接时,您只需要检查您的节点是否在同一个连接组件中。

另请注意,您必须检查您的原始文件是否已连接,因为在这种情况下,Kruskall 算法将找到生成林而不是生成树。

于 2012-04-03T05:47:40.077 回答
0

最强大的(如果不是最常见的)循环检测方法是通过 Tarjan 的 SCC(强连接组件)算法。无论如何,这个问题已经得到了回答:

在有向图中查找所有循环

于 2012-04-07T21:51:40.723 回答