Handout No: 20
[Handout berikutnya, sebelumnya,
atau kembali ke
halaman utama]
Struktur Data graf:
representasi & algoritma-algoritma
14 April 1999
Goal:
Rrepresentasi,
DFS,
BFS,
Shortest path Dijkstra
Disclaimer: pada tanggal 5 Mei
1999 page ini baru selesai 90% ....
Representasi Graph
Representasi Matriks Keterhubungan Langsung
(Adjacency Matrix)
Suatu matriks digunakan untuk menyatakan
adjacency set setiap verteks dalam baris-barisnya. Nomor baris menyatakan
nomor verteks adjacency berasal dan nomor kolom menunjukkan nomor verteks
kemana arah adjacency. Elemen matriks [x, y] berharga 1 bila terdapat sisi
dari x ke y dan berharga 0 bila tidak ada.
Harga biner ini memungkinkan penggunaan
1 bit untuk setiap sel matriks. Misalnya pada suatu graph dengan jumlah
verteks 48 dapat digunakan 6 byte perbaris (semua 38 baris, jadi diperlukan
48 x 6 byte). Untuk mengetahui harga elemen matrilks maka diperlukan operasi
shift-right serts operasi boolean and. Misalnya adjacency dari verteks
15 ke verteks 27 pada contoh 48 verteks di atas maka byte ke ë
27/8û
dari baris ke 15 di-shift-right sebanyak (27 mod 8) posisi lalu di-and-kan
dengan bilangan 1. Bila hasilnya 1 maka 15 adjacent ke 27, bila 0 maka
tidak.
|
Direpresentasikan dalam matriks sbb.
Dari\Ke | A | B | C | D |
E | F | G | H |
I | J | K | L | M |
A | - | 1 | 1 | 1 |
0 | 1 | 0 | 0 |
0 | 0 | 0 | 0 | 0 |
B | 1 | - | 1 | 0 |
0 | 0 | 0 | 1 |
0 | 0 | 0 | 0 | 0 |
C | 1 | 1 | - | 0 |
1 | 0 | 1 | 1 |
1 | 0 | 0 | 0 | 0 |
D | 1 | 0 | 0 | - |
1 | 1 | 1 | 0 |
0 | 0 | 1 | 1 | 0 |
E | 0 | 0 | 1 | 1 |
- | 1 | 0 | 0 |
0 | 0 | 0 | 0 | 0 |
F | 1 | 0 | 0 | 1 |
1 | - | 0 | 0 |
0 | 0 | 0 | 0 | 0 |
G | 0 | 0 | 1 | 1 |
0 | 0 | - | 0 |
1 | 0 | 1 | 0 | 0 |
H | 0 | 1 | 1 | 0 |
0 | 0 | 0 | - |
1 | 0 | 0 | 0 | 0 |
I | 0 | 0 | 1 | 0 |
0 | 0 | 1 | 1 |
- | 1 | 0 | 0 | 1 |
J | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
1 | - | 1 | 0 | 1 |
K | 0 | 0 | 0 | 1 |
0 | 0 | 1 | 0 |
0 | 1 | - | 1 | 0 |
L | 0 | 0 | 0 | 1 |
0 | 0 | 0 | 0 |
0 | 0 | 1 | - | 1 |
M | 0 | 0 | 0 | 0 |
0 | 0 | 0 | 0 |
1 | 1 | 0 | 1 | - |
|
Sudah tentu, representasi bit ini
hanya dapat digunakan untuk graph yang tidak berbobot. Untuk graph berbobot
maka diperlukan suatu matriks di mana setiap elemennya menyatakan harga
(atau harga-harga) bobot tersebut. Adjacency secara implisit terepresentasikan
dengan bobot-bobot ini. Bila harga bobot ¥
(tak hingga) maka berarti tidak terdapat sisi adjacent ybs.
Matriks ini digunakan baik untuk
undigraph maupub digraph. Untuk undigraph maka matriks merupakan matriks
simetris.
|
Dari\Ke | A | B | C | D |
E | F | G | H |
I | J | K | L | M |
A | - | 24 | 43 | 33 |
¥ | 31 | ¥ | ¥ |
¥ | ¥ | ¥ | ¥ | ¥ |
B | 24 | - | 18 | ¥ |
¥ | ¥ | ¥ | 45 |
¥ | ¥ | ¥ | ¥ | ¥ |
C | 43 | 18 | - | ¥ |
16 | ¥ | 22 | 35 |
15 | ¥ | ¥ | ¥ | ¥ |
D | 33 | ¥ | ¥ | - |
19 | 22 | 39 | ¥ |
¥ | ¥ | 13 | 27 | ¥ |
E | ¥ | ¥ | 16 | 19 |
- | 15 | ¥ | ¥ |
¥ | ¥ | ¥ | ¥ | ¥ |
F | 31 | ¥ | ¥ | 22 |
15 | - | ¥ | ¥ |
¥ | ¥ | ¥ | ¥ | ¥ |
G | ¥ | ¥ | 22 | 39 |
¥ | ¥ | - | ¥ |
21 | ¥ | 13 | ¥ | ¥ |
H | ¥ | 45 | 35 | ¥ |
¥ | ¥ | ¥ | - |
25 | ¥ | ¥ | ¥ | ¥ |
I | ¥ | ¥ | 15 | ¥ |
¥ | ¥ | 21 | 25 |
- | 19 | ¥ | ¥ | 35 |
J | ¥ | ¥ | ¥ | ¥ |
¥ | ¥ | ¥ | ¥ |
19 | - | 10 | ¥ | 15 |
K | ¥ | ¥ | ¥ | 13 |
¥ | ¥ | 13 | ¥ |
¥ | 10 | - | 19 | ¥ |
L | ¥ | ¥ | ¥ | 27 |
¥ | ¥ | ¥ | ¥ |
¥ | ¥ | 19 | - | 25 |
M | ¥ | ¥ | ¥ | ¥ |
¥ | ¥ | ¥ | ¥ |
35 | 15 | ¥ | 25 | - |
Representasi List Keterhubungan Langsung
(Adjacency List)
Mengingat bahwa rasio degree (atau outdegree
pada digraph) rata-rata verteks terhadap jumlah verteks dalam sejumlah
masalah adalah bisa sangat kecil maka representasi matriks tersebut akan
berupa matriks sparse yaitu sebagian besarnya berisikan bilangan nol (atau
bilangan ¥
pada weighted graph). Untuk kepentingan efisiensi ruang maka tiap baris
matriks tersebut digantikan list yang hanya berisikan verteks-verteks dalam
adjacency set Vx dari setiap verteks x.
Keseluruhan graph adalah array dari
verteks yang pada masing-masing verteksnya digunakan suatu struktur list
untuk menyimpan indeks-indeks dari verteks yang adjacent dari verteks yang
bersangkutan. Jika list menggunakan fixed array maka perlu disimpan pula
degree (atau out degree) yang menyatakan panjang dari list. Jika menggunakan
list berkait maka informasi degree ini tidak diperlukan.
Pada masalah yang sangat dinamis
di mana selama proses berlangsung bisa muncul verteks baru atau dihapusnya
suatu verteks yang ada dari graph maka struktur array untuk verteks ini
dapat menggunakan suatu list berkait pula. Pada struktur demikian maka
list dari adjacent verteks berisikan pointer dari verteks-verteks. Secara
keseluruhan apabila adjacent list menggunakan list berkait pula, graph
direpresentasikan dengan multilevel linked-list.
Algoritma-algoritma Pencarian
Pencarian atau yang mirip dengan pencarian
adalah proses-proses yang paling umum dalam masalah graph. Terdapat 2 metoda
pencarian:
-
Depth First Search (DFS): pada setiap
pencabangan, penelusuran verteks-verteks yang belum dikunjungi dilakukan
selengkapnya pada pencabangan pertama, kemudian selengkapnya pada pencabangan
kedua, dan seterusnya secara rekursif.
-
Breadth First Search (BFS): pada setiap
pencabangan penelusuran verteks-verteks yang belum dikunjungi dilakukan
pada verteks-verteks adjacent, kemudian berturut-turut selengkapnya pada
masing-masing pencabangan dari setiap verteks adjacent tersebut secara
rekursif.
Implementasi algoritma DFS rekursif
Rekursivitas dilakukan dalam fungsi
Traverse()
class Graph {
....
void DFS() {
for (each vertex v in G)
v.Status = false;
for (each vertex v in G)
if (v.Status == false)
Traverse(v);
}
void Traverse(Vertex
v) {
Vertex w;
w.Visit();
v.Status = true;
for (each adjac. vertex w from
v) {
if (w.Status == false)
Traverse(w);
}
}
}
}
Implementasi algoritma DFS nonrekursif
Memanfaatkan stack sebagai struktur
data pendukung
class Graph {
....
void DFS() {
Stack s = new Stack();
Vertex v, w, t;
for (each vertex v in G)
v.Status = false;
for (each vertex v in G) {
s.push(v);
while (s.emptyStack() == false){
w=s.pop();
if (v.Status == false) {
visit(w);
v.Status = true;
for (each adjac. vertex t from w)
s.push(t);
}
}
}
}
}
Implementasi algoritma BFS
Algoritma BFS menjadi kurang straightforward
jika dinyatakan secara rekursif. Jadi sebaiknya diimplementasikan secara
nonrekursif dengan memanfaatkan queue sebagai struktur data pendukung
class Graph {
....
void BFS() {
Queue q = new Queue();
Vertex v, w, t;
for (each vertex v in G)
v.Status = false;
for (each vertex v in G) {
if (v.Status == false) {
v.Status = true;
q.add(v);
while (EmptyQueue(Q) == false){
w = q.remove();
visit(w);
for (each adjac. vertex T from
w){
if (t.Status == false) {
t.Status = true;
q.add(t);
}
}
}
}
}
}
}
Masalah Pencarian Lintasan Terpendek
Pencarian shortest path (lintasan terpendek)
dalam adalah termasuk masalah yang paling umum dalam suatu weighted, connected
graph. Lebih lanjut lagi dalam kelas masalah ini terdapat subkelas-subkelas
masalah yang lebih spesifik. Misalnya pada jaringan jalan raya yang menghubungkan
kota-kota disuatu wilayah, hendak dicari
-
lintasan terpendek yag menghubungkan
antara dua kota berlainan tertentu (Single-source Single-destination Shortest
Path Problems)
-
semua lintasan terpendek masing-masing
dari suatu kota ke setiap kota lainnya (Single-source Shortest Path problems)
-
semua lintasan terpendek masing-masing
antara tiap kemungkinan pasang kota yang berbeda (All-pairs Shortest Path
Problems)
Untuk memecahkan masing-masing dari
masalah-masalah tersebut terdapat sejumlah solusi.
-
Algoritma Dijkstra untuk Single-source
Shortest Path
-
Algoritma Floyd-Warshall untuk masalah
All-pairs Shortest Path
-
Algoritma Johnson untuk masalah All-pairs
Shortest Path pada sparse graph
Dalam beberapa masalah graph lain,
suatu graph dapat memiliki bobot negatif dan kasus ini dipecahkan oleh
algoritma Bellman-Ford.
Yang akan dibahas di sini adalah
algoritma Dijkstra yaitu mencari lintasan terpendek dari suatu verteks
asal tertentu vs ke setiap verteks lainnya (digunakan juga dengan sangat
optimal pada masalah single-destination). Kelas-kelas masalah lain dibahas
dengan cukup rinci di Cormen et. al, Introduction to Algorithms, McGraw-Hill,
1990.
Algoritma Dijkstra
Algoritma ini mirip dengan algoritma
Prim untuk mencari MST, yaitu pada tiap iterasi memeriksa sisi-sisi yang
menghubungkan subset verteks W dan subset verteks (V-W) dan memindahkan
verteks w dari (V-W) ke W yang memenuhi kriteria tertentu. Perbedaannya
terletak pada kriteria itu sendiri.
-
Jika yang dicari algoritma Kruskal adalah
sisi dengan bobot terkecil dari sisi-sisi di atas dalam setiap iterasinya,
-
dalam algoritma Dijkstra, yang dicari
adalah sisi yang menghubungkan ke suatu verteks di (V-W) sehingga jarak
dari verteks asal Vs ke verteks tersebut adalah minimal.
Dalam implementasinya penghitungan jarak
dari verteks asal vs disederhanakan dengan menambahkan field minpath pada
setiap verteks; field-field minpath ini diinisialisasi sesuai dengan adjacencynya
dengan vs, kemudian dalam setiap iterasi di-update bersamaan masuknya w
dalam W. Field minpath ini menunjukkan jarak dari vs ke verteks yang bersangkutan
terpendek yang diketahui hingga saat itu. Jadi pada verteks dalam W, minpath
sudah menunjukkan jarak terpendek dari Vs untuk mencapai verteks yang bersangkutan,
sementara pada (V-W) masih perlu diupdate pada setiap iterasi dalam mendapatkan
verteks w seperti diterangkan di atas. Yaitu, setiap mendapatkan w maka
update minpath setiap adjacent verteks x dari w di (V-W) sisa dengan:
-
minimum (minpath dari x , total
minpath w + panjang sisi yang bersangkutan), agar dapat berlaku umum maka
di awal algoritma seluruh minpath diinisialisasi dengan +¥
.
Demikian halnya pencarian w itu sendiri
disederhanakan menjadi pencarian node di (V-W) dengan minpath terkecil.
Selengkapnya algoritma Dijkstra adalah
sebagai berikut.
-
Inisialisasi
-
W berisi mula-mula hanya vs
-
field minpath tiap verteks v dengan
-
Weight[vs, v], jika ada sisi tersebut,
atau,
-
+¥
, jika tidak ada.
-
Lalu, dalam iterasi lakukan hingga (V-W)
tak tersisa (atau dalam versi lain: jika ve ditemukan):
-
dari field minpath tiap verteks cari
verteks w dalam (V-W) yang memiliki minpath terkecil yang bukan tak hingga.
-
jika ada w maka
-
masukkan w dalam W
-
update minpath pada tiap verteks t adjacent
dari w dan berada dalam (V-W) dengan: minimum (minpath[t] , minpath[w]
+ Weight[w, t])
Verteks-verteks dalam W dapat dibedakan dari verteks dalam (V-W) dengan
suatu field yang berfungsi sebagai flag atau dengan struktur liked-list.
Informasi lintasan dapat diketahui dengan pencatatan predesesor dari setiap
verteks yang dilakukan pada saat update harga minpath tersebut (fungsi
minimum): jika minpath[t] diupdate dengan (minpath[w]+Weight[w, t]) maka
predesesor dari t adalah w. pada tahap inisialisasi predesesor setiap verteks
diisi oleh vs.
Contoh masalah:
Pada weighted graph di atas akan dicari shortest path dari A ke G. Algoritma
ini akan bekerja sbb. (Notasi penulisan: X:mvia, dibaca, vertex X
dicapai dari A melalui via dengan panjang path m, warna verteks
A menunjukkan status visit dari X = true, (update): menunjukkan terjadinya update harga minpath dan via dari verteks ybs.).
-
Pada tahap inisialisasi:
tabel MinPath diisi:
{A:0, B: 24A, C: 43A, D: 33A, F: 31A, yang lain ¥ }
-
Iterasi ke 1:
dari tabel minpath didapat B (via A) dan update tabel MinPath menjadi:
A:0, B: 24A, C: 42B(update), D: 33A, F: 31A, H: 69B(update),
yang lain ¥}
-
Iterasi ke 2:
dari tabel minpath didapat F (via A) dan update tabel MinPath menjadi:
A:0, B: 24A, C: 42B, D: 33A, E: 46B(update), F: 31A, H: 69B,
yang lain ¥}
-
Iterasi ke 3:
dari tabel minpath didapat D (via A) dan update tabel MinPath menjadi:
A:0, B: 24A, C: 42B, D: 33A, E: 46B, F: 31A, G: 72D(update), H: 69B, K: 46D(update), L: 60D(update),
yang lain ¥}
-
Iterasi ke 4:
dari tabel minpath didapat C (via B) dan update tabel MinPath menjadi:
A:0, B: 24A, C: 42B, D: 33A, E: 46B, F: 31A, G: 64D, H: 69B, I: 57C(update), K: 46D, L: 60D,
yang lain ¥}
-
Iterasi ke 5:
dari tabel minpath didapat E (via B) dan update tabel MinPath menjadi:
A:0, B: 24A, C: 42B, D: 33A, E: 46B, F: 31A, G: 64D, H: 69B, I: 57C, K: 46D, L: 60D,
yang lain ¥}
-
Iterasi ke 6:
dari tabel minpath didapat K (via D) dan update tabel MinPath menjadi:
A:0, B: 24A, C: 42B, D: 33A, E: 46B, F: 31A, G: 59K(update), H: 69B, I: 57C, J: 56K(update), K: 46D, L: 60D,
yang lain ¥}
-
Iterasi ke 7:
dari tabel minpath didapat J (via K) dan update tabel MinPath menjadi:
A:0, B: 24A, C: 42B, D: 33A, E: 46B, F: 31A, G: 59K, H: 69B, I: 57C, J: 56K, K: 46D, L: 60D, M: 71J(update),
yang lain ¥}
-
Iterasi ke 8:
dari tabel minpath didapat I (via C) dan update tabel MinPath menjadi:
A:0, B: 24A, C: 42B, D: 33A, E: 46B, F: 31A, G: 59K, H: 69B, I: 57C, J: 56K, K: 46D, L: 60D, M: 71J}
-
Iterasi ke 9:
dari tabel minpath didapat G (via K) dan update tabel MinPath menjadi:
A:0, B: 24A, C: 42B, D: 33A, E: 46B, F: 31A, G: 59K, H: 69B, I: 57C, J: 56K, K: 46D, L: 60D, M: 71J}
- Karena G adalah verteks tujuan maka algoritma berhenti dan shortest path dari A ke G diperoleh dengan panjang 59. Route ybs: A-D-K-G.
Berikut ini adalah applet demo Shortest path. Isikan asal dan tujuan dengan id dari verteks yang dicari atau melakukan click-and-drag pada verteks-verteks asal-tujuan. Tujuan bisa diisi tanda bintang (*) untuk mencari shortest path ke semua verteks (Lihat juga contoh dengan graph yang lebih besar!!!).
Handout berikutnya, sebelumnya, atau kembali ke halaman utama