🧩 7.10: Sıralama Örnek Sorular

Pratik Yaparak Öğrenin

📋 Bu Bölümde Neler Öğreneceksiniz?

Bu sayfada sıralama algoritmalarıyla ilgili klasik mülakat sorularını ve algoritma problemlerini çözeceğiz. Her problem için:

1

K'ıncı En Büyük Elemanı Bulma

Orta

Bir dizideki k'ıncı en büyük elemanı bulan fonksiyon yazın. Örneğin k=1 en büyük, k=2 ikinci en büyük demektir.

Örnek 1: arr = [3, 2, 1, 5, 6, 4], k = 2 → 5
Örnek 2: arr = [3, 2, 3, 1, 2, 4, 5, 5, 6], k = 4 → 4

🧠 Düşünce Süreci

  1. İlk Akla Gelen: Diziyi sırala ve sondan k'ıncı elemanı al - O(n log n)
  2. Daha İyi Çözüm: Min-Heap ile k elemanlı heap tut - O(n log k)
  3. En İyi Çözüm: QuickSelect (Quick Sort'un fikri) - O(n) average
  4. Karar: Pratik için sıralama yöntemi yeterli, ama mülakatta QuickSelect sorulabilir!
💡 İpucu: Python'da sorted(arr, reverse=True)[k-1] en kısa çözüm. Ama QuickSelect algoritmasını da öğrenmek faydalı!
🔍 Çözümü Göster/Gizle
K'ıncı En Büyük Eleman - Python
# =========================================
# K'INCI EN BÜYÜK ELEMANI BULMA
# =========================================

import heapq

def kinci_en_buyuk_siralama(arr, k):
    """
    Yöntem 1: Sıralama ile
    Zaman: O(n log n)
    Alan: O(n) (sort için)
    """
    return sorted(arr, reverse=True)[k-1]

def kinci_en_buyuk_heap(arr, k):
    """
    Yöntem 2: Min-Heap ile
    Zaman: O(n log k)
    Alan: O(k)
    
    Fikir: K elemanlı min-heap tut.
    - Yeni eleman heap'in minimum'undan büyükse, 
      minimum'u çıkar ve yeni elemanı ekle.
    - Sonunda heap'in minimum'u (root) k'ıncı en büyük!
    """
    # İlk k elemanla heap oluştur
    heap = arr[:k].copy()
    heapq.heapify(heap)
    
    # Kalan elemanları işle
    for i in range(k, len(arr)):
        if arr[i] > heap[0]:  # heap[0] = minimum
            heapq.heapreplace(heap, arr[i])
    
    return heap[0]

def quickselect(arr, k):
    """
    Yöntem 3: QuickSelect
    Zaman: O(n) average, O(n²) worst
    Alan: O(1)
    
    Quick Sort'un partitioning fikrini kullan,
    ama sadece k'ıncı elemanın olduğu tarafa git.
    """
    # k'ıncı en büyük = (n-k)'ıncı en küçük (0-indexed)
    target = len(arr) - k
    
    def partition(left, right, pivot_idx):
        pivot = arr[pivot_idx]
        # Pivot'u sona taşı
        arr[pivot_idx], arr[right] = arr[right], arr[pivot_idx]
        store_idx = left
        
        for i in range(left, right):
            if arr[i] < pivot:
                arr[i], arr[store_idx] = arr[store_idx], arr[i]
                store_idx += 1
        
        arr[store_idx], arr[right] = arr[right], arr[store_idx]
        return store_idx
    
    def select(left, right):
        if left == right:
            return arr[left]
        
        # Orta elemanı pivot olarak seç
        pivot_idx = (left + right) // 2
        pivot_idx = partition(left, right, pivot_idx)
        
        if target == pivot_idx:
            return arr[target]
        elif target < pivot_idx:
            return select(left, pivot_idx - 1)
        else:
            return select(pivot_idx + 1, right)
    
    return select(0, len(arr) - 1)

# ============ TEST ============
print("🎯 K'INCI EN BÜYÜK ELEMAN")
print("=" * 50)

arr1 = [3, 2, 1, 5, 6, 4]
k1 = 2

print(f"Dizi: {arr1}, k={k1}")
print(f"Sıralama ile: {kinci_en_buyuk_siralama(arr1, k1)}")
print(f"Heap ile: {kinci_en_buyuk_heap(arr1, k1)}")
print(f"QuickSelect ile: {quickselect(arr1.copy(), k1)}")

print()
arr2 = [3, 2, 3, 1, 2, 4, 5, 5, 6]
k2 = 4
print(f"Dizi: {arr2}, k={k2}")
print(f"Sıralama ile: {kinci_en_buyuk_siralama(arr2, k2)}")
Çıktı bekleniyor...

📊 Karmaşıklık Karşılaştırması

Yöntem Zaman Alan
Sıralama O(n log n) O(n)
Min-Heap O(n log k) O(k)
QuickSelect O(n) avg O(1)
2

Dutch National Flag (Renklere Göre Sırala)

Orta

Sadece 0, 1 ve 2 değerlerinden oluşan bir diziyi sıralayın. 0'lar başa, 1'ler ortaya, 2'ler sona gelmeli. Tek geçişte ve O(1) alan kullanarak çözün!

Örnek: [2, 0, 2, 1, 1, 0] → [0, 0, 1, 1, 2, 2]

🧠 Düşünce Süreci

  1. Naif Çözüm: Counting Sort - 0, 1, 2'leri say ve yeniden yaz. O(n) zaman, O(1) alan. İşe yarar!
  2. Mülakat Beklentisi: "In-place" ve "tek geçişte" isteniyor. Counting Sort 2 geçiş yapar!
  3. Three-way Partitioning: Quick Sort'tan esinlenme!
    • low: 0'ların sonu
    • mid: şu an baktığımız eleman
    • high: 2'lerin başlangıcı
  4. Algoritma:
    • 0 görünce: low ile swap, low++, mid++
    • 1 görünce: sadece mid++
    • 2 görünce: high ile swap, high-- (mid artmaz!)
Başlangıç: [2, 0, 2, 1, 1, 0]
           ↑              ↑
          low            high
          mid

mid=2 → high ile swap → [0, 0, 2, 1, 1, 2]
                                       high--

mid=0 → low ile swap, low++, mid++ → devam...

Sonuç: [0, 0, 1, 1, 2, 2] ✅
            
🔍 Çözümü Göster/Gizle
Dutch National Flag - Python
# =========================================
# DUTCH NATIONAL FLAG (3-WAY PARTITIONING)
# =========================================

def dutch_flag_counting(arr):
    """
    Yöntem 1: Counting Sort yaklaşımı
    Zaman: O(n) (2 geçiş)
    Alan: O(1)
    """
    count = [0, 0, 0]
    for x in arr:
        count[x] += 1
    
    idx = 0
    for num in range(3):
        for _ in range(count[num]):
            arr[idx] = num
            idx += 1
    return arr

def dutch_flag_partition(arr):
    """
    Yöntem 2: Three-way Partitioning (Dijkstra)
    Zaman: O(n) (TEK geçiş!)
    Alan: O(1)
    
    low: 0'ların biteceği yer (sol taraf)
    mid: şu an incelenen index
    high: 2'lerin başlayacağı yer (sağ taraf)
    """
    low = 0
    mid = 0
    high = len(arr) - 1
    
    print(f"Başlangıç: {arr}")
    print("-" * 40)
    
    while mid <= high:
        if arr[mid] == 0:
            # 0 görünce sola at
            arr[low], arr[mid] = arr[mid], arr[low]
            low += 1
            mid += 1
            print(f"0 bulundu, swap(low,mid): {arr}")
        elif arr[mid] == 1:
            # 1 yerinde, sadece ilerle
            mid += 1
            print(f"1 bulundu, mid++: {arr}")
        else:  # arr[mid] == 2
            # 2 görünce sağa at
            arr[mid], arr[high] = arr[high], arr[mid]
            high -= 1
            # mid artmaz! Swap edilen değeri kontrol etmeli
            print(f"2 bulundu, swap(mid,high): {arr}")
    
    return arr

# ============ TEST ============
print("🇳🇱 DUTCH NATIONAL FLAG")
print("=" * 50)

arr1 = [2, 0, 2, 1, 1, 0]
print(f"\n📍 Test 1: {arr1}")
dutch_flag_partition(arr1.copy())
print(f"Sonuç: {dutch_flag_partition([2, 0, 2, 1, 1, 0])}")

print("\n" + "=" * 50)
arr2 = [2, 0, 1]
print(f"\n📍 Test 2: {[2, 0, 1]}")
result = dutch_flag_partition(arr2)
print(f"Sonuç: {result}")
Çıktı bekleniyor...
3

Neredeyse Sıralı Diziyi Sırala

Orta

Bir dizi verilmiş ve her eleman olması gereken konumdan en fazla k adım uzakta. Bu diziyi en verimli şekilde sıralayın.

Örnek: arr = [6, 5, 3, 2, 8, 10, 9], k = 3 → [2, 3, 5, 6, 8, 9, 10]
Açıklama: 6, sıralı dizide index 3'te olmalı (0'dan uzaklık ≤ 3) ✓

🧠 Düşünce Süreci

  1. Gözlem: Her eleman max k uzaklıkta → ilk sıralı eleman ilk k+1 eleman arasında!
  2. Insertion Sort? Normalde O(n²), ama burada her eleman max k adım kayar → O(n·k)
  3. Min-Heap Çözümü: k+1 elemanlık min-heap tut!
    • Heap'ten minimum'u çıkar → sıralı diziye ekle
    • Diziden yeni eleman heap'e ekle
    • Her işlem O(log k)
  4. Sonuç: O(n log k) - k küçükse çok verimli!
💡 Neden k+1? En küçük eleman en kötü durumda k adım ileride olabilir. Yani ilk k+1 elemana bakmamız lazım!
🔍 Çözümü Göster/Gizle
Neredeyse Sıralı Dizi - Python
# =========================================
# NEREDEYSE SIRALI DİZİYİ SIRALAMA
# =========================================

import heapq

def sort_nearly_sorted_insertion(arr, k):
    """
    Yöntem 1: Insertion Sort
    Zaman: O(n * k)
    Alan: O(1)
    
    Her eleman max k adım kayar.
    """
    n = len(arr)
    for i in range(1, n):
        key = arr[i]
        j = i - 1
        # Maximum k adım geriye bak
        while j >= max(0, i - k) and arr[j] > key:
            arr[j + 1] = arr[j]
            j -= 1
        arr[j + 1] = key
    return arr

def sort_nearly_sorted_heap(arr, k):
    """
    Yöntem 2: Min-Heap
    Zaman: O(n * log k)
    Alan: O(k)
    
    k+1 elemanlık sliding window heap kullan.
    """
    n = len(arr)
    
    # İlk k+1 elemanla heap oluştur
    heap = arr[:k+1].copy()
    heapq.heapify(heap)
    
    result_idx = 0
    
    print(f"Başlangıç heap (ilk {k+1} eleman): {heap}")
    
    # Kalan elemanları işle
    for i in range(k + 1, n):
        # Heap'ten minimum'u çıkar ve sonuca ekle
        arr[result_idx] = heapq.heappop(heap)
        result_idx += 1
        
        # Yeni elemanı heap'e ekle
        heapq.heappush(heap, arr[i])
        print(f"Min çıktı: {arr[result_idx-1]}, heap: {heap}")
    
    # Kalan elemanları boşalt
    while heap:
        arr[result_idx] = heapq.heappop(heap)
        result_idx += 1
    
    return arr

# ============ TEST ============
print("📊 NEREDEYSE SIRALI DİZİYİ SIRALAMA")
print("=" * 50)

arr = [6, 5, 3, 2, 8, 10, 9]
k = 3

print(f"Orijinal: {arr}")
print(f"k = {k} (her eleman max {k} uzaklıkta)")
print()

# Heap yöntemi
result = sort_nearly_sorted_heap(arr.copy(), k)
print(f"\n✅ Sıralı (Heap): {result}")

# Insertion sort karşılaştırması
result2 = sort_nearly_sorted_insertion(arr.copy(), k)
print(f"✅ Sıralı (Insertion): {result2}")

print("\n" + "=" * 50)
print("⏱️ Karmaşıklık:")
print(f"   Insertion Sort: O(n*k) = O({len(arr)}*{k}) = O({len(arr)*k})")
print(f"   Min-Heap: O(n*log k) = O({len(arr)}*log{k}) ≈ O({len(arr)*2})")
Çıktı bekleniyor...
4

İki Sıralı Diziyi Birleştir

Kolay

İki sıralı dizi verilmiş. Bunları tek bir sıralı dizi olarak birleştirin. Bu Merge Sort'un temel adımıdır!

Örnek: arr1 = [1, 3, 5, 7], arr2 = [2, 4, 6, 8] → [1, 2, 3, 4, 5, 6, 7, 8]

🧠 Düşünce Süreci

  1. Two Pointer Tekniği: İki dizinin başından başla
  2. Her adımda küçük olanı sonuç dizisine ekle
  3. Eklenen pointer'ı bir ilerlet
  4. Bir dizi bitince diğerinin kalanını ekle
  5. Zaman: O(n + m), Alan: O(n + m)
🔍 Çözümü Göster/Gizle
İki Sıralı Diziyi Birleştir - Python
# =========================================
# İKİ SIRALI DİZİYİ BİRLEŞTİR (MERGE)
# =========================================

def merge_sorted_arrays(arr1, arr2):
    """
    Two Pointer ile birleştirme
    Zaman: O(n + m)
    Alan: O(n + m)
    """
    result = []
    i, j = 0, 0
    
    print(f"arr1: {arr1}")
    print(f"arr2: {arr2}")
    print("-" * 40)
    
    # İki dizi de bitene kadar karşılaştır
    while i < len(arr1) and j < len(arr2):
        if arr1[i] <= arr2[j]:
            result.append(arr1[i])
            print(f"arr1[{i}]={arr1[i]} eklendi")
            i += 1
        else:
            result.append(arr2[j])
            print(f"arr2[{j}]={arr2[j]} eklendi")
            j += 1
    
    # Kalan elemanları ekle
    while i < len(arr1):
        result.append(arr1[i])
        i += 1
    
    while j < len(arr2):
        result.append(arr2[j])
        j += 1
    
    return result

# ============ TEST ============
print("🔗 İKİ SIRALI DİZİYİ BİRLEŞTİR")
print("=" * 50)

arr1 = [1, 3, 5, 7]
arr2 = [2, 4, 6, 8]

result = merge_sorted_arrays(arr1, arr2)
print(f"\n✅ Birleşik: {result}")

print("\n" + "=" * 50)
print("\n📍 Farklı uzunluklar:")
arr3 = [1, 2, 3]
arr4 = [4, 5, 6, 7, 8, 9]
result2 = merge_sorted_arrays(arr3, arr4)
print(f"✅ Birleşik: {result2}")
Çıktı bekleniyor...
5

Aralıkları Birleştir (Merge Intervals)

Orta

Bir interval listesi verilmiş. Üst üste binen aralıkları birleştirin.

Örnek 1: [[1,3], [2,6], [8,10], [15,18]] → [[1,6], [8,10], [15,18]]
Örnek 2: [[1,4], [4,5]] → [[1,5]]

🧠 Düşünce Süreci

  1. Gözlem: Aralıklar sıralı değilse karışık! Önce başlangıç noktasına göre sırala.
  2. Birleştirme Kuralı: Önceki aralığın sonu ≥ sonrakinin başı ise birleştir.
  3. Algoritma:
    • Başlangıç noktasına göre sırala - O(n log n)
    • Sonuç listesine ilk aralığı ekle
    • Her yeni aralık için: öncekiyle birleşebilir mi kontrol et
[1,3]    [2,6]        [8,10]       [15,18]
|---|
     |------|
              birleşir!
                       |---|
                                   |---|

Sonuç: [1,6], [8,10], [15,18]
            
🔍 Çözümü Göster/Gizle
Merge Intervals - Python
# =========================================
# ARALIKLARI BİRLEŞTİR (MERGE INTERVALS)
# =========================================

def merge_intervals(intervals):
    """
    Örtüşen aralıkları birleştir
    Zaman: O(n log n) - sıralama
    Alan: O(n) - sonuç listesi
    """
    if not intervals:
        return []
    
    # Başlangıç noktasına göre sırala
    intervals.sort(key=lambda x: x[0])
    print(f"Sıralı: {intervals}")
    
    result = [intervals[0]]
    
    for i in range(1, len(intervals)):
        current = intervals[i]
        last = result[-1]
        
        # Birleşebilir mi? (öncekinin sonu >= sonrakinin başı)
        if last[1] >= current[0]:
            # Birleştir: sonuç aralığının sonunu güncelle
            result[-1][1] = max(last[1], current[1])
            print(f"{last} + {current} → birleşti: {result[-1]}")
        else:
            # Ayrı aralık
            result.append(current)
            print(f"{current} ayrı eklendi")
    
    return result

# ============ TEST ============
print("📅 MERGE INTERVALS")
print("=" * 50)

intervals1 = [[1,3], [2,6], [8,10], [15,18]]
print(f"Giriş: {intervals1}")
result1 = merge_intervals([x.copy() for x in intervals1])
print(f"✅ Sonuç: {result1}")

print("\n" + "=" * 50)
intervals2 = [[1,4], [4,5]]
print(f"\nGiriş: {intervals2}")
result2 = merge_intervals([x.copy() for x in intervals2])
print(f"✅ Sonuç: {result2}")

print("\n" + "=" * 50)
intervals3 = [[1,4], [0,4]]  # Sırasız
print(f"\nGiriş (sırasız): {intervals3}")
result3 = merge_intervals([x.copy() for x in intervals3])
print(f"✅ Sonuç: {result3}")
Çıktı bekleniyor...
6

Hangi Sıralama Algoritmasını Seçmeli?

Teori

Aşağıdaki senaryolarda hangi sıralama algoritmasını tercih edersiniz? Nedenini açıklayın.

📋 Senaryolar

  1. 10 elemanlı küçük bir dizi sıralamak
  2. Çok büyük (1 milyon eleman) rastgele dizi
  3. Neredeyse sıralı 100.000 elemanlı dizi
  4. Linked List sıralamak
  5. [0-100] arasında notları sıralamak
  6. Stability (kararlılık) gerekiyor
🔍 Cevapları Göster/Gizle
1. 10 elemanlı küçük dizi → Insertion Sort

n küçük olduğunda O(n²) bile hızlı. Overhead yok, basit.

2. 1 milyon rastgele eleman → Quick Sort veya Merge Sort

O(n log n) şart! Quick Sort pratikte daha hızlı, Merge Sort garantili.

3. Neredeyse sıralı dizi → Insertion Sort

Neredeyse sıralı için O(n)! Veya Tim Sort (Python'ın sorted'ı).

4. Linked List → Merge Sort

Random access yok, Quick Sort kötü çalışır. Merge Sort O(1) extra space ile yapılabilir.

5. [0-100] arası notlar → Counting Sort

k=101 çok küçük, O(n+k) ≈ O(n). En hızlı seçenek!

6. Stability gerekli → Merge Sort, Counting Sort, Radix Sort

Quick Sort ve Heap Sort stable değil! Python'ın sorted() stable.

📋 Bölüm Özeti

🎯 Sıralama Problemlerinde Önemli Noktalar

K'ıncı Eleman

QuickSelect O(n), Heap O(n log k)

3-way Partition

Dutch Flag, duplicate handling

Merge İşlemi

Two pointer tekniği

Doğru Algoritma

Veri karakteristiğine göre seç!