Algoritma Prim dengan Pelaksanaan Java

1. Pengenalan

Dalam tutorial ini, kita mula-mula mengetahui apa itu pokok rentang minimum. Selepas itu, kami akan menggunakan algoritma Prim untuk mencari satu.

2. Pokok Rentang Minimum

Pokok rentang minimum (MST) ialah graf yang dihubungkan dengan wajaran, tidak diarahkan, dan berat pinggir keseluruhannya telah dikurangkan dengan menghilangkan tepi yang lebih berat . Dengan kata lain, kita mengekalkan semua bucu grafik tetap utuh, tetapi kita mungkin membuang beberapa tepi sehingga jumlah semua tepi minimum.

Kita mulakan dengan graf berwajaran kerana tidak masuk akal untuk meminimumkan jumlah berat tepi jika pinggirnya sama sekali tidak mempunyai berat badan. Mari lihat contoh grafik:

Grafik di atas adalah graf yang dihubungkan dengan wajaran, tidak diarahkan. Berikut adalah MST graf itu:

Walau bagaimanapun, MST grafik tidak unik. Sekiranya graf mempunyai lebih daripada satu MST, maka setiap MST mempunyai jumlah berat tepi yang sama.

3. Algoritma Prim

Algoritma Prim mengambil graf yang berwajaran, tidak diarahkan, disambungkan sebagai input dan mengembalikan MST graf itu sebagai output .

Ia berfungsi secara tamak . Pada langkah pertama, ia memilih bucu sewenang-wenangnya. Selepas itu, setiap langkah baru menambahkan bucu terdekat ke pokok yang dibina sejauh ini sehingga tidak ada bucu terputus yang tersisa.

Mari jalankan algoritma Prim pada grafik ini langkah demi langkah:

Dengan mengandaikan bucu sewenang-wenangnya untuk memulakan algoritma adalah B, kita mempunyai tiga pilihan A, C, dan E untuk pergi. Berat tepi yang sesuai adalah 2, 2, dan 5, oleh itu minimum adalah 2. Dalam kes ini, kita mempunyai dua tepi dengan berat 2, jadi kita dapat memilih salah satu daripadanya (tidak kira yang mana). Mari pilih A:

Sekarang kita mempunyai pokok dengan dua bucu A dan B. Kita boleh memilih mana-mana tepi A atau B yang belum ditambahkan yang membawa ke bucu yang tidak ditambahkan. Jadi, kita boleh memilih AC, BC, atau BE.

Algoritma Prim memilih minimum, iaitu 2, atau BC:

Sekarang kita mempunyai pokok dengan tiga bucu dan tiga kemungkinan tepi untuk maju: CD, CE, atau BE. AC tidak disertakan kerana ia tidak akan menambahkan bucu baru ke pokok. Berat minimum di antara ketiga-tiganya ialah 1.

Walau bagaimanapun, terdapat dua sisi yang beratnya 1. Akibatnya, algoritma Prim memilih salah satu daripadanya (sekali lagi tidak kira yang mana) dalam langkah ini:

Hanya tinggal satu bucu untuk bergabung, jadi kita boleh memilih dari CE dan BE. Berat minimum yang dapat menghubungkan pokok kita dengannya ialah 1, dan algoritma Prim akan memilihnya:

Oleh kerana semua bucu grafik input kini terdapat di pohon output, algoritma Prim berakhir. Oleh itu, pokok ini adalah MST graf input.

4. Pelaksanaan

Garisan menegak dan tepi membuat grafik, jadi kita memerlukan struktur data untuk menyimpan unsur-unsur ini. Mari buat kelas Edge :

public class Edge { private int weight; private boolean isIncluded = false; }

Setiap Edge mesti mempunyai berat kerana algoritma Prim berfungsi pada graf berwajaran. isIncluded menunjukkan sama ada Edge terdapat di pokok minimum atau tidak.

Sekarang, mari tambah kelas Vertex :

public class Vertex { private String label = null; private Map edges = new HashMap(); private boolean isVisited = false; }

Setiap Vertex boleh mempunyai label secara pilihan . Kami menggunakan peta tepi untuk menyimpan hubungan di antara bucu. Akhirnya, isVisited menunjukkan sama ada titik puncak telah dikunjungi oleh algoritma Prim setakat ini atau tidak.

Mari buat kelas Prim kami di mana kami akan melaksanakan logiknya:

public class Prim { private List graph; }

Senarai bucu cukup untuk menyimpan keseluruhan graf kerana di dalam setiap Vertex , kita mempunyai Peta untuk mengenal pasti semua sambungan. Di dalam Prim, kami membuat kaedah run () :

public void run() { if (graph.size() > 0) { graph.get(0).setVisited(true); } while (isDisconnected()) { Edge nextMinimum = new Edge(Integer.MAX_VALUE); Vertex nextVertex = graph.get(0); for (Vertex vertex : graph) { if (vertex.isVisited()) { Pair candidate = vertex.nextMinimum(); if (candidate.getValue().getWeight() < nextMinimum.getWeight()) { nextMinimum = candidate.getValue(); nextVertex = candidate.getKey(); } } } nextMinimum.setIncluded(true); nextVertex.setVisited(true); } }

Kita mulakan dengan menetapkan elemen pertama grafik Senarai seperti yang dikunjungi. Unsur pertama boleh menjadi salah satu simpul bergantung pada susunan yang telah mereka tambahkan ke senarai di tempat pertama. isDisconnected () mengembalikan true sekiranya terdapat Vertex yang tidak dikunjungi setakat ini:

private boolean isDisconnected() { for (Vertex vertex : graph) { if (!vertex.isVisited()) { return true; } } return false; }

Walaupun pokok rentang minimum adalah Tidak Terputus () , kami melingkari bucu yang telah dikunjungi dan menemui Tepi dengan berat minimum sebagai calon untuk NextVertex:

public Pair nextMinimum() { Edge nextMinimum = new Edge(Integer.MAX_VALUE); Vertex nextVertex = this; Iterator
    
      it = edges.entrySet() .iterator(); while (it.hasNext()) { Map.Entry pair = it.next(); if (!pair.getKey().isVisited()) { if (!pair.getValue().isIncluded()) { if (pair.getValue().getWeight() < nextMinimum.getWeight()) { nextMinimum = pair.getValue(); nextVertex = pair.getKey(); } } } } return new Pair(nextVertex, nextMinimum); }
    

We find the minimum of all candidates in the main loop and store it in nextVertex. Then, we set nextVertex as visited. The loop goes on until all vertices are visited.

At the end, each Edge with isIncluded equal to true is present.

Note that since nextMinimum() iterates through the edges, the time complexity of this implementation is O(V2). If we store the edges in a priority queue (sorted by weight) instead, the algorithm will perform in O(E log V).

5. Testing

Okay, so now that we've got some code, let's test it with a real example. First, we construct our graph:

public static List createGraph() { List graph = new ArrayList(); Vertex a = new Vertex("A"); ... Vertex e = new Vertex("E"); Edge ab = new Edge(2); a.addEdge(b, ab); b.addEdge(a, ab); ... Edge ce = new Edge(1); c.addEdge(e, ce); e.addEdge(c, ce); graph.add(a); ... graph.add(e); return graph; }

Pembina daripada Prim kelas mengambil ia dan kedai-kedai di dalam kelas. Kita boleh mencetak grafik input dengan kaedah originalGraphToString () :

Prim prim = new Prim(createGraph()); System.out.println(prim.originalGraphToString());

Contoh kami akan menghasilkan:

A --- 2 --- B A --- 3 --- C B --- 5 --- E B --- 2 --- C C --- 1 --- E C --- 1 --- D

Sekarang, kami menjalankan algoritma Prim dan mencetak MST yang dihasilkan dengan kaedah minimumSpanningTreeToString () :

prim.run(); prim.resetPrintHistory(); System.out.println(prim.minimumSpanningTreeToString());

Akhirnya, kami mencetak MST kami:

A --- 2 --- B B --- 2 --- C C --- 1 --- E C --- 1 --- D

6. Kesimpulannya

Dalam artikel ini, kami mengetahui bagaimana algoritma Prim menemui pokok minimum bagi graf. Kod boleh didapati di GitHub.