8

最小スパニング ツリーを見つけるプログラムを作成しようとしています。しかし、このアルゴリズムで私が抱えている問題の 1 つは、回路のテストです。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 のアルゴリズムは、パフォーマンス効率が悪いため、サイクルを検索しません。しかし、ツリーであるコンポーネントを作成し、それらを相互に接続しようとします。ご存知のように、2 つの異なるツリーを 1 つの新しいエッジで接続すると、新しいツリーが作成され、サイクルをチェックする必要はありません。

ウィキページのアルゴリズムを見ると、次のようになります。

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.

これには、 Disjoint Set Data Structureを使用する必要があります。再びウィキから:

最初に O(E log E) 時間で比較ソートを使用して重みでエッジをソートします。これにより、「S から重みが最小のエッジを削除する」というステップを一定時間で実行できます。次に、素集合のデータ構造 (Union&Find) を使用して、どの頂点がどのコンポーネントにあるかを追跡します。O(E) 操作、2 つの「検索」操作、および場合によってはエッジごとに 1 つの結合を実行する必要があります。ランクごとのユニオンを使用した素集合フォレストなどの単純な素集合データ構造でさえ、O(E log V) 時間で O(E) 操作を実行できます。したがって、合計時間は O(E log E) = O(E log V) です。


まとまりのない森をつくる

これで、 Boost Graph Library-Incremental Components部分を見ることができます。いくつかのメソッドを実装する必要があります: MakeSetFindUnion、その後、クラスカルのアルゴリズムを実装できます。いくつかのセットを操作するだけで、最も簡単な方法は、リンクされたリストを使用することです。

各セットには、セット内の最初の要素である代表要素として名前が付けられた 1 つの要素があります。

1- まず、リンクされたリストによってMakeSetを実装します。

これにより、グラフ内の各頂点を独自のコンポーネント (またはセット) のメンバーにすることで、インクリメンタル連結コンポーネント アルゴリズム用の素集合データ構造が準備されます。

各頂点 (要素) を新しいセットの代表的な要素として初期化する必要があります。これは、それらを親として設定することで実行できます。

 function MakeSet(x)
   x.parent := x

2- Findメソッドを実装します。

頂点 を含む集合の代表要素を見つける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メソッドを使用してこれらのセットをマージします。各セットはツリーであるため、それらの結合はツリーです。

ばらばらなセットのより良いデータ構造を選択することでこれを最適化できますが、今のところは十分だと思います。より高度なデータ構造に興味がある場合は、ランクベースの方法を実装できます。wiki で見つけることができます。簡単ですが、当惑しないように言及しませんでした。

于 2012-03-31T23:51:44.387 に答える
2

頂点が何らかの方法でラベル付けされている場合は、選択したエッジの両方の頂点がループを示す以前にアクセスされたかどうかを確認するだけです。

したがって、整数で実装されている場合は、ブール配列を使用して、どの頂点が選択されたかをマークできます。

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

または、頂点が文字列としてラベル付けされている場合は、たとえばそれらをマップに追加して、まだ追加されていないことを確認できます。

于 2012-02-27T04:13:40.750 に答える
2

回路をチェックするには、union-find データ構造を使用する必要があります。これを行う最も簡単な方法は、リンクされたリストを使用することですが、より効率的な実装があります。このリンクは、自分で実装する場合に役立ちます。

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

または、Java 実装へのリンクを次に示します。

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

基本的に、union-find データ構造を使用すると、互いに素な要素のセットを追跡でき、次の 2 つの操作がサポートされます。

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は終了です。エッジの追加によってサイクルが作成されるかどうかを確認することは、それが接続する 2 つの頂点が既に存在するかどうかを確認することになりますC(2 つの操作を使用して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 (Strongly Connected Components) アルゴリズムを使用することです。いずれにせよ、この質問はすでに回答されています。

有向グラフのすべてのサイクルを見つける

于 2012-04-07T21:51:40.723 に答える