Contoh Fungsi Rekursif

By | August 16, 2025
Pengenalan Fungsi Rekursif

Fungsi rekursif adalah salah satu konsep fundamental dalam algoritma pemrograman lanjut yang memungkinkan sebuah fungsi memanggil dirinya sendiri. Konsep ini sangat powerful untuk menyelesaikan masalah yang dapat dipecah menjadi sub-masalah yang lebih kecil dengan pola yang sama.

Dalam artikel ini, kita akan mempelajari berbagai contoh fungsi rekursif mulai dari yang sederhana hingga kompleks, lengkap dengan implementasi dan analisis kompleksitasnya.

Konsep Dasar Rekursi

Komponen Rekursi

  • Base Case: Kondisi berhenti
  • Recursive Case: Pemanggilan diri sendiri
  • Progress: Menuju base case
  • Return Value: Nilai kembalian

Keuntungan Rekursi

  • Kode lebih bersih dan elegant
  • Mudah memahami logika
  • Natural untuk masalah tertentu
  • Implementasi algoritma divide-conquer

⚠️ Perhatian Penting

Rekursi tanpa base case yang tepat akan menyebabkan stack overflow. Selalu pastikan ada kondisi yang membuat rekursi berhenti!

Visualisasi Rekursi – Faktorial

factorial(4) = 4 × factorial(3)

factorial(3) = 3 × factorial(2)

factorial(2) = 2 × factorial(1)

factorial(1) = 1 × factorial(0)

factorial(0) = 1 (Base Case)

Contoh 1: Faktorial

Faktorial adalah contoh klasik rekursi. n! = n × (n-1)! dengan base case 0! = 1.

Python – Faktorial:

def faktorial(n):
    # Base case
    if n == 0 or n == 1:
        return 1
    
    # Recursive case
    return n * faktorial(n - 1)

# Contoh penggunaan
print(f"5! = {faktorial(5)}")  # Output: 120
print(f"0! = {faktorial(0)}")  # Output: 1

# Trace eksekusi faktorial(4):
# faktorial(4) = 4 * faktorial(3)
# faktorial(3) = 3 * faktorial(2) 
# faktorial(2) = 2 * faktorial(1)
# faktorial(1) = 1 (base case)
# Hasil: 4 * 3 * 2 * 1 = 24

 

JavaScript – Faktorial:

function faktorial(n) {
    // Base case
    if (n === 0 || n === 1) {
        return 1;
    }
    
    // Recursive case
    return n * faktorial(n - 1);
}

// Contoh penggunaan
console.log(`5! = ${faktorial(5)}`); // Output: 120
console.log(`3! = ${faktorial(3)}`); // Output: 6

Contoh 2: Fibonacci

Deret Fibonacci adalah contoh rekursi dengan dua pemanggilan rekursif. Untuk memahami lebih dalam tentang optimasi algoritma rekursif, mari lihat implementasi dan optimasinya.

Fibonacci Dasar (Tidak Efisien):

def fibonacci(n):
    # Base cases
    if n <= 1:
        return n
    
    # Recursive case
    return fibonacci(n - 1) + fibonacci(n - 2)

# Contoh penggunaan
for i in range(10):
    print(f"F({i}) = {fibonacci(i)}")

# Output: 0, 1, 1, 2, 3, 5, 8, 13, 21, 34

 

Fibonacci dengan Memoization (Efisien):

def fibonacci_memo(n, memo={}):
    # Cek apakah sudah dihitung sebelumnya
    if n in memo:
        return memo[n]
    
    # Base cases
    if n <= 1:
        return n
    
    # Hitung dan simpan hasil
    memo[n] = fibonacci_memo(n - 1, memo) + fibonacci_memo(n - 2, memo)
    return memo[n]

# Contoh penggunaan
print(f"F(50) = {fibonacci_memo(50)}")  # Sangat cepat!

📊 Perbandingan Kompleksitas

  • Fibonacci Dasar: O(2^n) – Exponential time
  • Fibonacci Memoization: O(n) – Linear time
  • Space Complexity: O(n) untuk menyimpan memo

Contoh 3: Binary Search

Binary search adalah algoritma pencarian yang sangat efisien menggunakan pendekatan divide-and-conquer dengan rekursi.

Binary Search Rekursif:

def binary_search(arr, target, left=0, right=None):
    if right is None:
        right = len(arr) - 1
    
    # Base case: element tidak ditemukan
    if left > right:
        return -1
    
    # Hitung middle index
    mid = (left + right) // 2
    
    # Base case: element ditemukan
    if arr[mid] == target:
        return mid
    
    # Recursive cases
    if target < arr[mid]:
        return binary_search(arr, target, left, mid - 1)
    else:
        return binary_search(arr, target, mid + 1, right)

# Contoh penggunaan
data = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
target = 7

result = binary_search(data, target)
if result != -1:
    print(f"Element {target} ditemukan di index {result}")
else:
    print(f"Element {target} tidak ditemukan")

Contoh 4: Tree Traversal

Rekursi sangat natural untuk operasi pada struktur data tree. Untuk pemahaman yang lebih mendalam tentang struktur data tree dan graph, mari lihat implementasi traversal.

Tree Node dan Traversal:

class TreeNode:
    def __init__(self, val=0, left=None, right=None):
        self.val = val
        self.left = left
        self.right = right

def inorder_traversal(root):
    """Left -> Root -> Right"""
    if root is None:
        return []
    
    result = []
    result.extend(inorder_traversal(root.left))   # Left
    result.append(root.val)                       # Root
    result.extend(inorder_traversal(root.right))  # Right
    
    return result

def preorder_traversal(root):
    """Root -> Left -> Right"""
    if root is None:
        return []
    
    result = []
    result.append(root.val)                        # Root
    result.extend(preorder_traversal(root.left))   # Left
    result.extend(preorder_traversal(root.right))  # Right
    
    return result

def postorder_traversal(root):
    """Left -> Right -> Root"""
    if root is None:
        return []
    
    result = []
    result.extend(postorder_traversal(root.left))   # Left
    result.extend(postorder_traversal(root.right))  # Right
    result.append(root.val)                         # Root
    
    return result

# Contoh penggunaan
#       1
#      / \
#     2   3
#    / \
#   4   5

root = TreeNode(1)
root.left = TreeNode(2)
root.right = TreeNode(3)
root.left.left = TreeNode(4)
root.left.right = TreeNode(5)

print("Inorder:", inorder_traversal(root))    # [4, 2, 5, 1, 3]
print("Preorder:", preorder_traversal(root))  # [1, 2, 4, 5, 3]
print("Postorder:", postorder_traversal(root)) # [4, 5, 2, 3, 1]

Contoh 5: Permutasi dan Kombinasi

Rekursi sangat powerful untuk generating permutasi dan kombinasi dari suatu set data.

 

Generate Permutations:

def generate_permutations(arr):
    # Base case
    if len(arr) <= 1:
        return [arr]
    
    result = []
    for i in range(len(arr)):
        # Ambil element saat ini
        current = arr[i]
        # Sisa elements
        remaining = arr[:i] + arr[i+1:]
        
        # Generate permutations dari sisa elements
        for perm in generate_permutations(remaining):
            result.append([current] + perm)
    
    return result

# Contoh penggunaan
data = [1, 2, 3]
perms = generate_permutations(data)
print("Permutations:")
for perm in perms:
    print(perm)

# Output:
# [1, 2, 3], [1, 3, 2], [2, 1, 3], 
# [2, 3, 1], [3, 1, 2], [3, 2, 1]

 

Generate Combinations:

def generate_combinations(arr, r):
    # Base cases
    if r == 0:
        return [[]]
    if len(arr) < r:
        return []
    
    result = []
    
    # Include first element
    first = arr[0]
    rest = arr[1:]
    
    # Combinations yang include first element
    for combo in generate_combinations(rest, r - 1):
        result.append([first] + combo)
    
    # Combinations yang tidak include first element
    result.extend(generate_combinations(rest, r))
    
    return result

# Contoh penggunaan
data = [1, 2, 3, 4]
combos = generate_combinations(data, 2)
print("Combinations (r=2):")
for combo in combos:
    print(combo)

# Output: [1,2], [1,3], [1,4], [2,3], [2,4], [3,4]

Best Practices dan Tips

✅ Do’s (Yang Harus Dilakukan)

  • Selalu definisikan base case yang jelas
  • Pastikan setiap recursive call menuju base case
  • Gunakan memoization untuk optimasi jika diperlukan
  • Pertimbangkan iterative solution untuk deep recursion
  • Test dengan input edge cases (0, 1, negative numbers)

❌ Don’ts (Yang Harus Dihindari)

  • Jangan lupakan base case (menyebabkan infinite recursion)
  • Hindari rekursi yang terlalu dalam (stack overflow)
  • Jangan gunakan rekursi untuk masalah yang lebih efisien dengan iterasi
  • Hindari duplicate computation tanpa memoization
  • Jangan abaikan space complexity dari call stack

Analisis Kompleksitas

Algoritma Time Complexity Space Complexity Catatan
Faktorial O(n) O(n) Linear recursion
Fibonacci (basic) O(2^n) O(n) Exponential – tidak efisien
Fibonacci (memo) O(n) O(n) Dengan memoization
Binary Search O(log n) O(log n) Divide and conquer
Tree Traversal O(n) O(h) h = height of tree

Frequently Asked Questions (FAQ)

Kapan sebaiknya menggunakan rekursi dibanding iterasi?

Gunakan rekursi ketika masalah memiliki struktur rekursif natural (seperti tree traversal, divide-conquer), atau ketika solusi rekursif lebih mudah dipahami. Gunakan iterasi untuk masalah sederhana atau ketika performance dan memory usage menjadi concern utama.

Apa itu tail recursion dan mengapa penting?

Tail recursion adalah rekursi dimana pemanggilan rekursif adalah operasi terakhir dalam fungsi. Beberapa compiler dapat mengoptimasi tail recursion menjadi loop, sehingga menghemat stack space. Namun Python tidak melakukan optimasi ini secara default.

Bagaimana cara menghindari stack overflow dalam rekursi?

Beberapa cara: 1) Pastikan base case yang benar, 2) Gunakan iterasi untuk deep recursion, 3) Implementasi trampolines atau continuation-passing style, 4) Increase stack limit (tidak direkomendasikan), 5) Gunakan memoization untuk mengurangi depth.

Apa perbedaan direct dan indirect recursion?

Direct recursion: fungsi memanggil dirinya sendiri langsung (seperti factorial). Indirect recursion: fungsi A memanggil fungsi B, dan fungsi B memanggil kembali fungsi A. Indirect recursion lebih kompleks dan sulit di-debug.

Bagaimana cara mengkonversi rekursi menjadi iterasi?

Gunakan stack atau queue eksplisit untuk menyimpan state yang biasanya disimpan di call stack. Untuk tail recursion, bisa langsung dikonversi ke loop. Untuk rekursi yang lebih kompleks, perlu simulate call stack dengan data structure.

Kesimpulan

Fungsi rekursif adalah tool yang powerful dalam pemrograman, terutama untuk masalah yang memiliki struktur rekursif natural. Dengan memahami konsep base case, recursive case, dan optimasi seperti memoization, Anda dapat mengimplementasikan solusi yang elegant dan efisien.

Praktik yang konsisten dengan berbagai contoh rekursi akan meningkatkan kemampuan problem-solving dan pemahaman algoritma yang lebih mendalam. Ingat untuk selalu mempertimbangkan trade-off antara readability dan performance.

Leave a Reply

Your email address will not be published. Required fields are marked *