ALGORITMA DFS (Depth-First Search)
Pengertian Algoritma DFS
Bayangkan Anda adalah seorang manajer proyek yang memiliki 5 pekerjaan (A, B, C, D, dan E) yang harus diselesaikan oleh satu mesin. Anda tahu setiap pekerjaan memiliki durasi dan tingkat kepentingan (bobot) yang berbeda. Tugas Anda adalah menemukan satu urutan kerja yang paling efisien, yaitu yang menghasilkan "total biaya" paling minimum.
Masalahnya, ada sangat banyak kemungkinan urutan (permutasi). Bagaimana Anda bisa yakin menemukan yang terbaik? Di sinilah Algoritma DFS (Depth-First Search) berperan sebagai strategi Anda.
Alih-alih bingung memikirkan semua kemungkinan sekaligus, Anda memutuskan untuk mencoba satu urutan penuh dari awal sampai akhir. Misalnya, Anda mencoba urutan A → B → C → D → E. Anda selesaikan simulasi untuk urutan ini sampai tuntas dan menghitung total biayanya. Inilah yang disebut pendekatan "Depth-First" atau "mendahulukan kedalaman"; Anda menjelajahi satu cabang kemungkinan secara mendalam hingga ke ujungnya.
Setelah mendapatkan biaya untuk urutan pertama, Anda tidak berhenti. Anda mundur satu langkah (backtracking) ke pekerjaan terakhir (E), lalu menukarnya dengan pilihan lain yang mungkin, misalnya A → B → C → E → D. Anda hitung lagi biayanya dan membandingkannya dengan biaya terbaik yang sudah Anda catat.
Proses ini terus berlanjut. Anda secara sistematis akan "mundur" dan mencoba cabang-cabang baru dari titik persimpangan terakhir, lalu menelusurinya lagi sampai ke dalam. Seperti yang dilakukan oleh fungsi cariUrutan dalam kode, algoritma ini secara rekursif akan mencoba setiap kemungkinan jalur sampai semua kombinasi urutan telah dieksplorasi.
Pada akhirnya, setelah semua cabang "pohon kemungkinan" telah dijelajahi, Anda akan memiliki satu urutan yang tercatat sebagai urutanTerbaik dengan biayaMinimum. Dengan cara ini, DFS menjamin bahwa Anda telah memeriksa setiap skenario dan menemukan solusi yang paling optimal.
Karakteristik Algoritma DFS
-
Memprioritaskan Kedalaman
Karakteristik paling mendasar. DFS akan selalu mencoba menjelajahi sejauh dan sedalam mungkin ke satu arah sebelum akhirnya mundur untuk mencoba arah lain.
-
Menggunakan Struktur Data Stack (Tumpukan)
Secara implementasi, DFS menggunakan mekanisme LIFO (Last-In, First-Out) yang mirip dengan tumpukan, baik secara eksplisit maupun melalui rekursi.
-
Membutuhkan Memori yang Lebih Sedikit
Dibandingkan dengan BFS, DFS umumnya lebih hemat memori karena hanya perlu menyimpan satu jalur dari titik awal hingga titik saat ini.
-
Bisa Menemukan Solusi yang Sangat Jauh dengan Cepat
Jika solusi berada di ujung sebuah cabang yang dalam, DFS bisa menemukannya dengan sangat cepat karena sifatnya yang langsung menelusuri ke dalam.
-
Tidak Menjamin Menemukan Jalur Terpendek
Ini adalah kelemahan utamanya. Karena DFS menjelajah secara "mendalam buta", jalur pertama yang ditemukannya belum tentu merupakan jalur terpendek.
Contoh Kasus: Penjadwalan Pekerjaan
Deskripsi Masalah
Cerita: Ada 5 pekerjaan yang harus dikerjakan 1 mesin secara berurutan. Setiap pekerjaan punya waktu pengerjaan dan bobot. Kita ingin mengatur urutan kerja agar "total waktu selesai × bobot" sekecil mungkin.
Data Pekerjaan (Durasi, Bobot):- A: (3 jam, bobot 4)
- B: (2 jam, bobot 2)
- C: (5 jam, bobot 10)
- D: (1 jam, bobot 1)
- E: (4 jam, bobot 3)
Tujuan: Cari urutan pekerjaan supaya total "waktu selesai × bobot" minimum.
Pseudocode
- Mulai
- Cari semua kemungkinan urutan pekerjaan.
- Untuk setiap urutan, hitung total biayanya.
- Bandingkan semua total biaya dan temukan yang paling kecil.
- Tampilkan urutan dengan biaya terkecil.
- Selesai
Implementasi Kode Java
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class JadwalKerjaOOP {
Map<String, int[]> jobs = new HashMap<>();
long biayaMinimum = Long.MAX_VALUE;
List<String> urutanTerbaik = new ArrayList<>();
public JadwalKerjaOOP() {
jobs.put("A", new int[]{3, 4});
jobs.put("B", new int[]{2, 2});
jobs.put("C", new int[]{5, 10});
jobs.put("D", new int[]{1, 1});
jobs.put("E", new int[]{4, 3});
}
void cariUrutan(List<String> urutanSekarang, List<String> sisaPekerjaan) {
if (sisaPekerjaan.isEmpty()) {
long biayaSekarang = hitungBiaya(urutanSekarang);
if (biayaSekarang < this.biayaMinimum) {
this.biayaMinimum = biayaSekarang;
this.urutanTerbaik = new ArrayList<>(urutanSekarang);
}
return;
}
for (String pekerjaan : new ArrayList<>(sisaPekerjaan)) {
urutanSekarang.add(pekerjaan);
sisaPekerjaan.remove(pekerjaan);
cariUrutan(urutanSekarang, sisaPekerjaan);
sisaPekerjaan.add(pekerjaan);
urutanSekarang.remove(urutanSekarang.size() - 1);
}
}
long hitungBiaya(List<String> urutan) {
long totalBiaya = 0;
long waktuSelesai = 0;
for (String namaPekerjaan : urutan) {
int durasi = this.jobs.get(namaPekerjaan)[0];
int bobot = this.jobs.get(namaPekerjaan)[1];
waktuSelesai += durasi;
totalBiaya += waktuSelesai * bobot;
}
return totalBiaya;
}
public static void main(String[] args) {
JadwalKerjaOOP program = new JadwalKerjaOOP();
List<String> namaPekerjaan = new ArrayList<>(program.jobs.keySet());
System.out.println("--- Mencari Jadwal Kerja Terbaik (Versi OOP) ---");
program.cariUrutan(new ArrayList<>(), namaPekerjaan);
System.out.println("\n--- Hasil Akhir ---");
System.out.println("Urutan terbaik: " + program.urutanTerbaik);
System.out.println("Biaya minimum: " + program.biayaMinimum);
}
}
Kesimpulan
Kode tersebut menggunakan algoritma DFS untuk menyelesaikan masalah penjadwalan kerja. Algoritma ini bekerja dengan cara mencoba setiap kemungkinan urutan pekerjaan satu per satu secara mendalam. Untuk setiap urutan yang lengkap, kode akan menghitung total biayanya, lalu membandingkannya dengan biaya terbaik yang pernah ditemukan. Proses ini menjamin penemuan urutan kerja yang paling optimal dengan total biaya minimum setelah semua kemungkinan dieksplorasi.
