Graph adalah struktur data yang merepresentasikan hubungan antar objek. Setiap objek disebut node atau vertex, dan hubungan antar node disebut edge. Graph dipakai di mana-mana: peta jalan (kota = node, jalan = edge), jaringan sosial (orang = node, pertemanan = edge), ketergantungan task (task = node, dependensi = edge). Pada chapter ini kita akan mengimplementasikan dua representasi graph: adjacency matrix (array 2D) dan adjacency list (array of linked list), lalu menjalankan BFS untuk traversal.
Konsep Graph #
Beberapa tipe graph yang umum:
- Directed: edge punya arah (A→B tidak sama dengan B→A).
- Undirected: edge tidak berarah (A-B sama dengan B-A).
- Weighted: edge punya bobot (jarak, biaya, waktu).
- Unweighted: edge hanya ada/tidak, tanpa bobot.
- Cyclic: ada siklus (path dari node kembali ke dirinya sendiri).
- Acyclic: tidak ada siklus (DAG = Directed Acyclic Graph).
Pemilihan representasi memengaruhi kompleksitas operasi:
- Adjacency matrix: cek edge
O(1), iterasi tetanggaO(V). MemoriO(V^2). Cocok untuk graph padat (banyak edge). - Adjacency list: cek edge
O(deg(v)), iterasi tetanggaO(deg(v)). MemoriO(V+E). Cocok untuk graph jarang (sedikit edge).
Representasi Adjacency Matrix #
Adjacency matrix adalah array 2D matriks[V][V]. matriks[i][j] berisi bobot edge i→j, atau 0 bila tidak ada edge. Untuk graph undirected, matrix simetris: matriks[i][j] == matriks[j][i].
#include <stdio.h>
#include <stdlib.h>
#define JUMLAH_NODE 6
typedef struct {
int matriks[JUMLAH_NODE][JUMLAH_NODE];
int jumlah_node;
} graph_matrix;
void gm_init(graph_matrix *g, int n) {
g->jumlah_node = n;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
g->matriks[i][j] = 0;
}
void gm_tambah_edge(graph_matrix *g, int dari, int ke, int berat) {
if (dari < 0 || dari >= g->jumlah_node) return;
if (ke < 0 || ke >= g->jumlah_node) return;
g->matriks[dari][ke] = berat;
}
void gm_cetak(graph_matrix *g) {
printf("Adjacency Matrix (%d node):\n ", g->jumlah_node);
for (int j = 0; j < g->jumlah_node; j++) printf("%3d", j);
printf("\n");
for (int i = 0; i < g->jumlah_node; i++) {
printf("%3d:", i);
for (int j = 0; j < g->jumlah_node; j++) {
printf("%3d", g->matriks[i][j]);
}
printf("\n");
}
}
int gm_tetangga(graph_matrix *g, int node, int *out, int maks) {
int count = 0;
for (int j = 0; j < g->jumlah_node && count < maks; j++) {
if (g->matriks[node][j] != 0) out[count++] = j;
}
return count;
}
int main(void) {
graph_matrix g;
gm_init(&g, JUMLAH_NODE);
/* Bangun graph directed */
gm_tambah_edge(&g, 0, 1, 1);
gm_tambah_edge(&g, 0, 2, 1);
gm_tambah_edge(&g, 1, 3, 1);
gm_tambah_edge(&g, 2, 3, 1);
gm_tambah_edge(&g, 3, 4, 1);
gm_tambah_edge(&g, 3, 5, 1);
gm_tambah_edge(&g, 4, 5, 1);
gm_cetak(&g);
int tetangga[10];
int n = gm_tetangga(&g, 3, tetangga, 10);
printf("\nTetangga node 3: ");
for (int i = 0; i < n; i++) printf("%d ", tetangga[i]);
printf("\n");
return 0;
}Output yang Dihasilkan:
Adjacency Matrix (6 node):
0 1 2 3 4 5
0: 0 1 1 0 0 0
1: 0 0 0 1 0 0
2: 0 0 0 1 0 0
3: 0 0 0 0 1 1
4: 0 0 0 0 0 1
5: 0 0 0 0 0 0
Tetangga node 3: 4 5 Matrix jelas terlihat sebagai grid 6x6. Node 3 terhubung ke node 4 dan 5 (matriks[3][4]=1, matriks[3][5]=1). Untuk graph directed, matrix tidak simetris—matriks[0][1]=1 tetapi matriks[1][0]=0.
Mari kita telaah baris-baris kode di atas:
- Baris 6-9 (
graph_matrixstruct): array 2D statis dengan ukuranJUMLAH_NODE x JUMLAH_NODE. Fieldjumlah_nodemelacak jumlah node aktif (boleh kurang dariJUMLAH_NODE). - Baris 11-16 (
gm_init): reset semua sel ke 0 (tidak ada edge). Loop nested sederhana. - Baris 18-22 (
gm_tambah_edge): cek batas, lalu setmatriks[dari][ke] = berat. Untuk graph undirected, tambahkan barisg->matriks[ke][dari] = berat;juga. - Baris 24-37 (
gm_cetak): cetak matrix sebagai grid. Header kolom di atas, label baris di kiri. Format%3dmerapikan alignment. - Baris 39-45 (
gm_tetangga): iterasi barismatriks[node][j]untuk semuaj, kumpulkan indeksjyang tidak 0. Berguna untuk BFS/DFS. - Baris 47-62 (main): bangun graph directed dengan 7 edge. Cetak matrix. Cetak tetangga node 3.
Representasi Adjacency List #
Adjacency list menyimpan daftar tetangga tiap node sebagai linked list. Setiap node punya pointer ke head list tetangga. Memori O(V + E)—hemat pada graph jarang.
#include <stdio.h>
#include <stdlib.h>
typedef struct edge {
int tujuan;
int berat;
struct edge *next;
} edge;
typedef struct {
edge **adj;
int jumlah_node;
} graph_list;
void gl_init(graph_list *g, int n) {
g->jumlah_node = n;
g->adj = calloc((size_t)n, sizeof(edge *));
}
void gl_tambah_edge(graph_list *g, int dari, int ke, int berat) {
if (dari < 0 || dari >= g->jumlah_node) return;
if (ke < 0 || ke >= g->jumlah_node) return;
edge *e = malloc(sizeof(edge));
if (e == NULL) return;
e->tujuan = ke;
e->berat = berat;
e->next = g->adj[dari];
g->adj[dari] = e;
}
void gl_hapus_semua(graph_list *g) {
for (int i = 0; i < g->jumlah_node; i++) {
edge *cur = g->adj[i];
while (cur != NULL) {
edge *tmp = cur->next;
free(cur);
cur = tmp;
}
g->adj[i] = NULL;
}
free(g->adj);
g->adj = NULL;
}
void gl_cetak(graph_list *g) {
printf("Adjacency List (%d node):\n", g->jumlah_node);
for (int i = 0; i < g->jumlah_node; i++) {
printf(" %d ->", i);
for (edge *e = g->adj[i]; e != NULL; e = e->next) {
printf(" %d(w=%d)", e->tujuan, e->berat);
}
printf("\n");
}
}
/* BFS sederhana */
void bfs(graph_list *g, int mulai) {
int *dikunjungi = calloc((size_t)g->jumlah_node, sizeof(int));
int *antrian = malloc(sizeof(int) * (size_t)g->jumlah_node);
int depan = 0, belakang = 0;
printf("BFS mulai dari %d: ", mulai);
antrian[belakang++] = mulai;
dikunjungi[mulai] = 1;
while (depan < belakang) {
int cur = antrian[depan++];
printf("%d ", cur);
for (edge *e = g->adj[cur]; e != NULL; e = e->next) {
if (!dikunjungi[e->tujuan]) {
dikunjungi[e->tujuan] = 1;
antrian[belakang++] = e->tujuan;
}
}
}
printf("\n");
free(dikunjungi);
free(antrian);
}
int main(void) {
graph_list g;
gl_init(&g, 6);
gl_tambah_edge(&g, 0, 1, 1);
gl_tambah_edge(&g, 0, 2, 1);
gl_tambah_edge(&g, 1, 3, 1);
gl_tambah_edge(&g, 2, 3, 1);
gl_tambah_edge(&g, 3, 4, 1);
gl_tambah_edge(&g, 3, 5, 1);
gl_tambah_edge(&g, 4, 5, 1);
gl_cetak(&g);
printf("\n");
bfs(&g, 0);
bfs(&g, 3);
gl_hapus_semua(&g);
return 0;
}Output yang Dihasilkan:
Adjacency List (6 node):
0 -> 2(w=1) 1(w=1)
1 -> 3(w=1)
2 -> 3(w=1)
3 -> 5(w=1) 4(w=1)
4 -> 5(w=1)
5 ->
BFS mulai dari 0: 0 2 1 3 5 4
BFS mulai dari 3: 3 5 4 Adjacency list lebih ringkas dari matrix—hanya edge yang ada yang disimpan. Node 0 terhubung ke 2 dan 1 (dalam urutan terbalik karena insert di depan linked list). BFS dari node 0 mengunjungi: 0, lalu 2 dan 1 (tetangga), lalu 3 (tetangga 2/1), lalu 5 dan 4 (tetangga 3).
Mari kita telaah baris-baris kode di atas:
- Baris 4-8 (
struct edge): node linked list dengantujuan,berat, dannext. Setiap edge direpresentasikan satu node. - Baris 10-14 (
graph_liststruct): array ofedge *. Setiap indeksadj[i]adalah pointer ke head linked list tetangga nodei. Fieldjumlah_nodemelacak ukuran array. - Baris 16-19 (
gl_init): alokasi arrayadjdengancallocagar semua pointer awalnyaNULL.callocmengisi memori dengan 0, ekuivalen denganNULLuntuk pointer. - Baris 21-30 (
gl_tambah_edge): alokasi node edge baru, isi field, sisip di depan listadj[dari]. Insert depanO(1), urutan edge pada list terbalik dari urutan insert. - Baris 32-44 (
gl_hapus_semua): iterasi setiap node, bebaskan linked list tetangga, lalu bebaskan arrayadjitu sendiri. Wajib free dua tingkat: edges dulu, lalu array of pointers. - Baris 56-78 (
bfs): Breadth-First Search. Mulai dari nodemulai, masukkan ke queue. Loop: dequeue node, cetak, enqueue semua tetangga yang belum dikunjungi. Arraydikunjungimencegah kunjungan ganda. KompleksitasO(V + E). - Baris 75-76 (free di akhir BFS): bebaskan array
dikunjungidanantrianagar tidak leak bila BFS dipanggil berulang.
Perbandingan Representasi #
| Operasi | Adjacency Matrix | Adjacency List |
|---|---|---|
| Cek edge (u, v) | O(1) | O(deg(u)) |
| Iterasi tetangga | O(V) | O(deg(u)) |
| Memori | O(V^2) | O(V + E) |
| Tambah edge | O(1) | O(1) (insert depan) |
| Hapus edge | O(1) | O(deg(u)) |
| Cocok untuk | Graph padat (E ≈ V^2) | Graph jarang (E « V^2) |
Algoritma Graph Umum #
Beberapa algoritma graph klasik dan kompleksitasnya:
| Algoritma | Kompleksitas (adj list) | Kegunaan |
|---|---|---|
| BFS | O(V + E) | Shortest path unweighted, konektivitas |
| DFS | O(V + E) | Deteksi siklus, topological sort |
| Dijkstra | O((V + E) log V) | Shortest path weighted (non-negatif) |
| Bellman-Ford | O(V * E) | Shortest path dengan edge negatif |
| Kruskal | O(E log E) | Minimum Spanning Tree |
| Floyd-Warshall | O(V^3) | All-pairs shortest path |