Menu
CoddyTech

Rekursi (Recursion)

Terakhir diperbarui

Rekursi adalah fungsi yang memanggil dirinya sendiri pada versi yang lebih kecil dari masalah yang sama, sampai mencapai kasus yang cukup kecil untuk dijawab secara langsung. Kasus yang bisa dijawab langsung itu disebut base case, dan setiap fungsi rekursif membutuhkannya: fib(n) terus bercabang menjadi fib(n - 1) dan fib(n - 2) sampai mencapai fib(1) atau fib(0), yang langsung mengembalikan dirinya sendiri. Visualisasi di atas menjalankan persis hal ini: tekan Putar dan lihat pemanggilan bercabang menjadi pohon, mencapai base case di leaf, lalu mengembalikan nilainya ke atas dan digabungkan di setiap level.

Hal kedua yang ditampilkan animasi adalah call stack: setiap pemanggilan yang sudah dimulai tetapi belum kembali. Stack bertambah saat pemanggilan makin dalam, mencapai puncaknya di kedalaman rekursi, lalu mengurai saat hasil kembali. Itulah sebabnya rekursi yang dalam bisa terkena stack overflow, sedangkan loop iteratif tidak pernah menambah stack. Bentuk pemanggilan yang sama menggerakkan depth-first search, merge sort, dan sebagian besar operasi pada binary tree.

Kompleksitas waktu dan ruang

Untuk Fibonacci rekursif naif yang ditampilkan di atas, dan dua perbaikan standarnya:

PendekatanWaktuRuangCatatan
Rekursi naifO(2^n)O(n)Pohon pemanggilan berlipat dua di setiap level; ruangnya adalah stack terdalam, bukan seluruh pohon.
Dengan memoizationO(n)O(n)Setiap fib(k) dihitung sekali lalu disimpan; subtree yang berulang menyusut menjadi pencarian di cache.
Loop iteratifO(n)O(1)Dua variabel yang terus diperbarui menggantikan stack sepenuhnya.
Rekursi apa pun, secara umumjumlah pemanggilan × kerja per pemanggilanO(max depth)Stack menyimpan satu frame per pemanggilan yang sudah dimulai tetapi belum kembali.

Langkah demi langkah

LangkahApa yang terjadi
1Pemanggilan pertama fib(n) masuk ke call stack.
2Pemanggilan itu butuh fib(n - 1), jadi pemanggilan itu juga masuk ke stack; induknya menunggu.
3Pemanggilan terus bersarang sampai ada yang menanyakan n <= 1: base case langsung menjawab, tanpa pemanggilan yang lebih dalam.
4Nilai dari base case kembali ke induknya, yang sekarang boleh memulai pemanggilan keduanya, fib(n - 2).
5Saat kedua anaknya sudah kembali, induk menjumlahkan keduanya lalu ikut kembali; frame-nya keluar dari stack.
6Proses kembali ini berulang ke atas pohon sampai frame pemanggilan pertama keluar dengan jawaban akhir dan stack kosong.

Contoh penerapan

Mengevaluasi fib(4) sesuai urutan pemanggilan yang persis, seperti yang diputar animasi:

PemanggilanStack saat ituMengembalikan
fib(4)fib(4)menunggu anak-anaknya
fib(3)fib(4) > fib(3)menunggu anak-anaknya
fib(2)fib(4) > fib(3) > fib(2)menunggu anak-anaknya
fib(1)fib(4) > fib(3) > fib(2) > fib(1)1 (base case)
fib(0)fib(4) > fib(3) > fib(2) > fib(0)0 (base case)
fib(2) menggabungkanfib(4) > fib(3) > fib(2)1 + 0 = 1
fib(1)fib(4) > fib(3) > fib(1)1 (base case)
fib(3) menggabungkanfib(4) > fib(3)1 + 1 = 2
fib(2) lagifib(4) > fib(2)1, dihitung ulang dari awal
fib(4) menggabungkanfib(4)2 + 1 = 3

Kapan memakai rekursi

Pakai saatHindari saat
Masalahnya mirip dengan dirinya sendiri: pohon, struktur bersarang, divide and conquerLoop sederhana bisa menyatakan hal yang sama tanpa frame stack
Kedalamannya terbatas dan wajar, seperti O(log n) pada merge sortKedalamannya bisa mencapai ukuran input pada input yang sangat besar, sehingga berisiko stack overflow
Backtracking butuh stack untuk mengingat tempat melanjutkanSubmasalah yang sama berulang dan kamu tidak menyimpannya di cache
Versi rekursifnya jelas lebih mudah dibaca dan diverifikasiKamu berada di loop yang sangat sering dijalankan, di mana beban pemanggilan fungsi terasa nyata

Kode Recursion

Implementasi Recursion yang rapi dan siap dijalankan dalam Python, JavaScript, Java, C++, C. Pilih bahasa, salin kodenya, atau buka langsung di Coddy Playground.

Kode Recursion dalam Python

Python
1calls = 02
3def fib(n, depth=0):4    global calls5    calls += 16    # Print the call with its depth so the recursion is visible7    print("  " * depth + f"fib({n})")8    if n <= 1:9        return n10    return fib(n - 1, depth + 1) + fib(n - 2, depth + 1)11
12
13print("fib(5) =", fib(5))14print("calls made:", calls)
Jalankan kode ini di Playground Python

FAQ Rekursi

Apa itu base case pada rekursi?
Input yang cukup kecil untuk dijawab tanpa pemanggilan rekursif lagi. Untuk fib(n), base case-nya adalah n <= 1, yang langsung mengembalikan n. Tanpa base case yang bisa dicapai, pemanggilan tidak pernah berhenti, stack terus bertambah, dan program crash karena stack overflow.
Apa itu call stack dan mengapa penting?
Runtime menyimpan satu frame per pemanggilan yang sudah dimulai tetapi belum kembali, berisi argumen dan variabel lokalnya. Kedalaman rekursi sama dengan tinggi stack, jadi rekursi sedalam n level memakai memori O(n) meskipun setiap pemanggilan hampir tidak melakukan apa-apa. Deretan chip di bawah animasi menampilkan persis stack ini bertambah dan mengurai.
Mengapa Fibonacci rekursif butuh waktu eksponensial?
Karena submasalah yang sama dihitung ulang berkali-kali: pada contoh penerapan di atas, fib(2) dievaluasi dua kali di dalam fib(4), dan duplikasinya kira-kira berlipat dua di setiap level, sehingga menghasilkan O(2^n) pemanggilan. Menyimpan setiap hasil saat pertama kali dihitung, yang disebut memoization, menyusutkan pohonnya menjadi O(n).
Apakah rekursi lebih baik daripada iterasi?
Tidak ada yang selalu lebih baik. Setiap rekursi bisa ditulis ulang sebagai loop dengan stack eksplisit, dan setiap loop sebagai rekursi. Rekursi unggul dalam keterbacaan untuk masalah yang mirip dengan dirinya sendiri seperti penelusuran pohon dan depth-first search; iterasi unggul dalam memori dan beban pemanggilan untuk proses yang linear.
Apa penyebab stack overflow pada fungsi rekursif?

Bisa karena base case tidak ada atau tidak bisa dicapai, sehingga pemanggilan tidak pernah berhenti, atau karena rekursi yang benar tetapi kedalamannya terlalu besar untuk batas stack runtime, seperti berekursi sekali per elemen pada input berisi jutaan elemen. Solusinya adalah memastikan base case tercapai, membatasi kedalaman, atau mengubahnya menjadi iterasi.

Algoritma apa saja yang secara alami bersifat rekursif?
Algoritma pengurutan divide and conquer seperti merge sort dan quicksort, penelusuran binary tree dan graf, binary search, puzzle backtracking seperti N-queens, dan apa pun yang didefinisikan atas struktur bersarang seperti JSON atau sistem file.
Ilustrasi bahasa pemrograman di Coddy

Kuasai algoritma bersama Coddy

Mulai