2012-02-27 1 views
9

Tôi đang cố gắng viết một chương trình có thể tìm thấy cây bao trùm tối thiểu. Nhưng một vấn đề tôi gặp phải với thuật toán này, là thử nghiệm cho một mạch. Điều gì sẽ là cách tốt nhất để làm điều này trong java.Kiểm tra mạch khi thực hiện thuật toán Kruskalls

Ok đây là mã của tôi

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")); 

    } 
} 
+1

Đúng tôi nếu tôi sai nhưng đây là một cái cây, vì vậy tất cả các nút trừ nút đầu tiên sẽ có một nút cha. Bạn đã xem xét việc triển khai này chưa. –

+1

@Legend, Trong thuật toán của kruskall, trong thời gian chạy thuật toán, chúng ta có rừng không phải cây, vì vậy giả thiết của bạn là sai. –

Trả lời

5

Thuật toán của Kruskall sẽ không tìm kiếm chu kỳ vì nó không hiệu quả về hiệu suất; Nhưng cố gắng tạo ra một thành phần là cây, và sau đó kết nối chúng với nhau. Như bạn biết nếu bạn kết nối hai cây khác nhau với một cạnh mới, bạn sẽ tạo ra cây mới và không cần phải kiểm tra chu kỳ.

Nếu bạn nhìn vào wiki page thuật toán là như sau:

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. 

Bạn nên sử dụng Disjoint Set Data Structure cho việc này. một lần nữa từ wiki:

đầu tiên sắp xếp các cạnh theo trọng lượng bằng cách sử dụng một loại so sánh trong thời gian O (E log E) ; điều này cho phép các bước "loại bỏ một cạnh với trọng lượng tối thiểu từ S" để hoạt động trong thời gian không đổi. Tiếp theo, chúng tôi sử dụng cấu trúc phân chia cấu trúc (Union & Tìm) để theo dõi các đỉnh nào trong đó các thành phần . Chúng ta cần phải thực hiện các hoạt động O (E), hai hoạt động 'tìm' ' và có thể là một liên minh cho mỗi cạnh. Ngay cả một cấu trúc phân chia đơn giản, cấu trúc như các khu rừng tách rời với công đoàn theo thứ hạng có thể thực hiện các hoạt động OO (E) trong thời gian O (E log V). Do đó tổng thời gian là O (E log E) = O (E log V).


Tạo rời nhau Rừng

Bây giờ bạn có thể có một cái nhìn tại Boost Graph Library-Incremental Components phần. Bạn nên thực hiện một số phương pháp: MakeSet, Tìm, Union, Sau đó bạn có thể thực hiện thuật toán của kruskall. Tất cả những gì bạn đang làm là làm việc với một số bộ và cách đơn giản nhất có thể để làm như vậy là sử dụng danh sách được liên kết.

Mỗi bộ có một phần tử có tên là phần tử đại diện là phần tử đầu tiên trong tập hợp.

1- Đầu tiên thực hiện MakeSet bằng danh sách liên kết:

này chuẩn bị rời nhau-bộ cấu trúc dữ liệu cho gia tăng kết nối thuật toán linh kiện bằng cách làm cho mỗi đỉnh trong đồ thị thành viên của thành phần riêng của mình (hoặc thiết lập).

Bạn chỉ cần khởi tạo mỗi đỉnh (element) là một yếu tố đại diện của bộ mới, bạn có thể làm điều này bằng cách thiết lập chúng như bản thân mẹ:

function MakeSet(x) 
    x.parent := x 

2- Thực hiện Tìm phương pháp:

Tìm yếu tố đại diện của bộ, trong đó có đỉnh x:

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

Phần if kiểm tra phần tử có phải là phần tử đại diện hay không. chúng tôi thiết lập tất cả các yếu tố đại diện của tập hợp làm yếu tố đầu tiên của chúng bằng cách đặt chúng làm cha mẹ.

3 Và cuối cùng khi bạn có tất cả mọi thứ trước phần đơn giản được thực hiện Liên minh phương pháp:

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 

Bây giờ làm thế nào bạn nên chạy Kruskall?

Trước tiên hãy đặt tất cả các nút trong n bộ chia tách theo phương thức MakeSet. Trong mỗi bước sau khi tìm cạnh mong muốn (không được chọn và tối thiểu), tìm các tập hợp các đỉnh điểm cuối của nó bằng cách Tìm phương thức (các phần tử đại diện của chúng), nếu chúng giống nhau, thả cạnh này ra vì cạnh này gây ra chu kỳ, nhưng Nếu chúng nằm trong các bộ khác nhau, hãy sử dụng phương pháp Union để hợp nhất các bộ này. Bởi vì mỗi bộ là cây công đoàn của họ là cây.

Bạn có thể tối ưu hóa điều này bằng cách chọn cấu trúc dữ liệu tốt hơn cho các bộ phân tách, nhưng hiện tại tôi nghĩ là đủ. Nếu bạn quan tâm đến cấu trúc dữ liệu nâng cao hơn, bạn có thể thực hiện xếp hạng cách cơ bản, bạn sẽ tìm thấy nó trong wiki, Thật dễ dàng nhưng tôi đã không đề cập đến nó để ngăn chặn sự bối rối.

3

Nếu các đỉnh được dán nhãn trong một cách nào đó tất cả các bạn cần làm là kiểm tra xem cả hai đỉnh của cạnh được chọn đã được truy cập trước đó mà sẽ chỉ ra một vòng lặp.

Vì vậy, nếu nó được thực hiện với số nguyên bạn có thể sử dụng một mảng boolean để đánh dấu các đỉnh đã được chọn.

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

Hoặc nếu các đỉnh được gắn nhãn là chuỗi bạn có thể thêm chúng vào Bản đồ ví dụ và kiểm tra xem chúng chưa được thêm vào chưa.

2

Để kiểm tra mạch, bạn sẽ muốn sử dụng cấu trúc dữ liệu kết hợp. Cách dễ nhất để làm điều đó là chỉ với các danh sách được liên kết, nhưng có nhiều triển khai hiệu quả hơn. Liên kết này có thể giúp bạn nếu bạn muốn tự mình triển khai.

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

Hoặc đây là một liên kết đến một thực java:

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

Về cơ bản, một cấu trúc dữ liệu công đoàn-tìm phép bạn theo dõi các tập hợp không giao nhau của các yếu tố, và hỗ trợ hai hoạt động:

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 

Giả sử mảng cạnh của bạn tạo thành MST là S.Ý tưởng là bạn có thể tạo một tập hợp, C, sử dụng Union-Find, theo dõi các đỉnh của biểu đồ được kết nối bằng các cạnh trong S. Khi C chứa tất cả các đỉnh trong biểu đồ, sau đó bạn đã hoàn thành và kiểm tra xem liệu việc thêm cạnh sẽ tạo ra một số chu kỳ để kiểm tra xem hai đỉnh mà nó kết nối đã có trong C hay không (bằng cách sử dụng hai hoạt động Find).

Vì vậy, nếu E là tập hợp của tất cả các cạnh trong đồ thị, hoạt động cập nhật của bạn có thể tiến hành như sau:

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 

Và bạn ngừng cập nhật một lần C chứa tất cả các đỉnh của đồ thị.

0

Nếu bạn kiểm tra chu trình, sử dụng DFS nó sẽ rất không hiệu quả. Nhưng bạn có thể sử dụng Disjoint Set. Khi kết nối, bạn chỉ cần kiểm tra xem các nút của bạn có nằm trong cùng một thành phần được kết nối hay không. Cũng cần lưu ý rằng bạn phải kiểm tra xem bản gốc của bạn có được kết nối không, bởi vì thuật toán Kruskall sẽ tìm thấy rừng bao trùm chứ không phải là cây bao trùm trong trường hợp đó.

0

Cách mạnh nhất nếu không phổ biến nhất để phát hiện chu kỳ là thông qua thuật toán SCC (Thành phần được kết nối mạnh) của Tarjan. Trong mọi trường hợp, câu hỏi này đã được trả lời:

Finding all cycles in a directed graph