0Pricing
DSA Interview Prep · Pelajaran

Median dari Aliran Data dan Penggabungan K Arah

Pertahankan dua heap (max-heap untuk separuh kecil dan min-heap untuk separuh besar) untuk pembaruan median O(log n), lalu gabungkan k list terurut menggunakan heap.

Median dari Aliran Data dan Penggabungan K Arah adalah pelajaran DSA Interview Prep gratis di CoddyKit. Ini adalah pelajaran 4 dari 4. Kamu bisa membaca pelajaran lengkapnya di bawah secara gratis — lalu praktikkan langsung di browser dengan editor kode bawaan dan tutor AI 24/7. Ini adalah bagian dari jalur belajar DSA Interview Prep, dan progresmu tersinkronisasi di web dan aplikasi CoddyKit. Kursus DSA Interview Prep mencakup 4 pelajaran total.

Masalah Median dari Aliran Data

Menemukan Median dari Aliran Data (LeetCode #295) meminta Anda mendukung dua operasi secara efisien: addNum(num) untuk menambahkan angka dan findMedian() untuk mengembalikan median saat ini. Median dari list dengan panjang genap adalah rata-rata dari dua nilai tengah. List terurut dengan pendekatan brute force memerlukan penyisipan O(n) dan median O(1). Solusi optimal menggunakan dua tumpukan untuk penyisipan O(log n) dan median O(1).

import heapq

# Strategy: maintain two halves of the data
# max_heap: lower half (stores negated values for max behavior)
# min_heap: upper half
# Invariant: len(max_heap) == len(min_heap) or len(max_heap) == len(min_heap) + 1
# Invariant: max(max_heap) <= min(min_heap)
# Median:
#   odd count:  max_heap[0] (top of lower half)
#   even count: average of tops of both halves
print('Two-heap strategy for O(log n) insert, O(1) median')

Implementasi MedianFinder dengan Dua Tumpukan

Pertahankan tumpukan maksimum untuk separuh bawah dan tumpukan minimum untuk separuh atas. Selalu pastikan tumpukan maksimum memiliki ukuran yang sama dengan tumpukan minimum atau satu elemen lebih banyak. Saat menambahkan angka, lakukan push ke tumpukan maksimum, lalu seimbangkan dengan memindahkan elemen teratas tumpukan maksimum ke tumpukan minimum jika elemen tersebut lebih besar daripada elemen minimum tumpukan minimum, kemudian seimbangkan kembali ukurannya jika diperlukan.

import heapq

class MedianFinder:
    def __init__(self):
        self.lo = []  # max-heap (negated) for lower half
        self.hi = []  # min-heap for upper half

    def addNum(self, num):
        heapq.heappush(self.lo, -num)   # push to lower half
        # Ensure max of lower <= min of upper
        if self.hi and -self.lo[0] > self.hi[0]:
            heapq.heappush(self.hi, -heapq.heappop(self.lo))
        # Balance sizes: lo can have at most 1 more than hi
        if len(self.lo) > len(self.hi) + 1:
            heapq.heappush(self.hi, -heapq.heappop(self.lo))
        elif len(self.hi) > len(self.lo):
            heapq.heappush(self.lo, -heapq.heappop(self.hi))

    def findMedian(self):
        if len(self.lo) > len(self.hi):
            return -self.lo[0]  # odd count: top of lower half
        return (-self.lo[0] + self.hi[0]) / 2

mf = MedianFinder()
for n in [1, 2, 3, 4, 5]: mf.addNum(n)
print(mf.findMedian())  # 3.0

Menelusuri Langkah-Langkah MedianFinder

Memahami alasan invarian dua tumpukan dipertahankan sangat penting untuk menjelaskan solusi dalam wawancara. Mari telusuri penambahan [5, 15, 1, 3] langkah demi langkah. Setelah setiap penyisipan, seimbangkan tumpukan sehingga tumpukan maksimum bawah menyimpan separuh nilai yang lebih kecil. Invarian ini memastikan max(lo) <= min(hi) selalu terpenuhi, sehingga median dapat diakses dengan mudah di bagian teratas salah satu atau kedua tumpukan.

import heapq

# Manual trace for [5, 15, 1, 3]:
# add 5:   lo=[-5]        hi=[]       median=5
# add 15:  lo=[-5]        hi=[15]     median=(5+15)/2=10
# add 1:   lo=[-5,-1]     hi=[15]     median=5
# add 3:   lo=[-5,-3,-1]  hi=[15]     -- lo too big
#       -> lo=[-5,-3]      hi=[1,15]  -- wait, wrong direction
# Actually:
# add 1:   push to lo -> lo=[-5,-1], then 1>lo? No, -lo[0]=5>15? No
#          lo has 2, hi has 1: balance -> move lo top to hi
#          lo=[-1], hi=[5,15]
# Median = (-lo[0] + hi[0])/2 = (1+5)/2 = 3
mf2 = MedianFinder()
for n, expected in [(5, 5.0), (15, 10.0), (1, 5.0), (3, 4.0)]:
    mf2.addNum(n)
    print(f'After adding {n}: median={mf2.findMedian()} (expected ~{expected})')

Median Jendela Geser

Median Jendela Geser (LeetCode #480) merupakan varian yang lebih sulit: temukan median setiap jendela berukuran k saat jendela tersebut bergeser melintasi array. Pendekatan dua tumpukan diperluas dengan himpunan penghapusan tertunda untuk menangani elemen yang keluar dari jendela. Saat sebuah elemen meninggalkan jendela, tandai elemen tersebut dalam himpunan penghapusan; saat elemen itu mencapai bagian teratas salah satu tumpukan, buang elemen tersebut.

import heapq

def median_sliding_window(nums, k):
    lo = []  # max-heap (negated)
    hi = []  # min-heap
    removed = {}
    result = []

    def balance():
        # Move valid tops to correct side
        while lo and removed.get(-lo[0], 0) > 0:
            removed[-lo[0]] -= 1; heapq.heappop(lo)
        while hi and removed.get(hi[0], 0) > 0:
            removed[hi[0]] -= 1; heapq.heappop(hi)

    for i, num in enumerate(nums):
        heapq.heappush(lo, -num)
        heapq.heappush(hi, -heapq.heappop(lo))
        if len(hi) > len(lo): heapq.heappush(lo, -heapq.heappop(hi))
        if i >= k:
            out = nums[i - k]
            removed[out] = removed.get(out, 0) + 1
        balance()
        if len(lo) > len(hi): heapq.heappush(hi, -heapq.heappop(lo))
        if i >= k - 1:
            if len(lo) > len(hi): result.append(float(-lo[0]))
            else: result.append((-lo[0] + hi[0]) / 2.0)
    return result

print(median_sliding_window([1,3,-1,-3,5,3,6,7], 3))  # [1,-1,-1,3,5,6]

Penggabungan K-Arah: Masalahnya

Menggabungkan K Daftar Terurut (LeetCode #23) merupakan masalah fundamental dengan penerapan dalam pengurutan eksternal, penggabungan basis data, dan sistem terdistribusi. Diberikan k daftar tertaut terurut dengan total n simpul, gabungkan semuanya menjadi satu daftar terurut. Pendekatan naif, yaitu menggabungkan dua daftar sekaligus, memiliki kompleksitas O(kn), atau O(n log k) dengan pendekatan bagi-dan-taklukkan. Pendekatan tumpukan memproses setiap simpul tepat satu kali dengan pekerjaan O(log k) per simpul, sehingga totalnya O(n log k).

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

# Build a linked list from a Python list
def build_list(arr):
    dummy = ListNode(0)
    curr = dummy
    for val in arr:
        curr.next = ListNode(val)
        curr = curr.next
    return dummy.next

# Convert linked list to Python list for printing
def to_list(head):
    result = []
    while head:
        result.append(head.val)
        head = head.next
    return result

print('K-way merge: O(n log k) using a min-heap of k heads')

Penggabungan K-Arah dengan Tumpukan Minimum

Inisialisasi tumpukan dengan simpul pertama dari setiap daftar. Pada setiap langkah, lakukan pop terhadap elemen minimum, tambahkan elemen tersebut ke hasil, lalu lakukan push terhadap simpul berikutnya dari daftar tersebut jika ada. Tumpukan selalu memiliki paling banyak k elemen—satu kepala untuk setiap daftar aktif. Karena total n simpul diproses dengan operasi tumpukan O(log k) untuk masing-masing simpul, waktu totalnya adalah O(n log k) dan ruang yang diperlukan untuk tumpukan adalah O(k).

import heapq

def merge_k_lists(lists):
    dummy = ListNode(0)
    curr = dummy
    heap = []
    for i, node in enumerate(lists):
        if node:
            heapq.heappush(heap, (node.val, i, node))
    while heap:
        val, i, node = heapq.heappop(heap)
        curr.next = node
        curr = curr.next
        if node.next:
            heapq.heappush(heap, (node.next.val, i, node.next))
    return dummy.next

lists = [
    build_list([1, 4, 5]),
    build_list([1, 3, 4]),
    build_list([2, 6])
]
result = merge_k_lists(lists)
print(to_list(result))  # [1, 1, 2, 3, 4, 4, 5, 6]

Rentang Terkecil yang Mencakup K Daftar

Rentang Terkecil (LeetCode #632) mencari rentang [lo, hi] terkecil sehingga setidaknya satu elemen dari setiap k daftar terurut berada di dalam rentang tersebut. Gunakan tumpukan minimum yang diinisialisasi dengan elemen pertama dari setiap daftar dan lacak nilai maksimum saat ini. Perkecil rentang dengan selalu memajukan daftar yang memiliki nilai minimum saat ini. Berhenti ketika salah satu daftar habis.

import heapq

def smallest_range(nums):
    heap = []
    current_max = float('-inf')
    for i, lst in enumerate(nums):
        heapq.heappush(heap, (lst[0], i, 0))
        current_max = max(current_max, lst[0])
    best = [float('-inf'), float('inf')]
    while heap:
        current_min, list_idx, elem_idx = heapq.heappop(heap)
        if current_max - current_min < best[1] - best[0]:
            best = [current_min, current_max]
        if elem_idx + 1 >= len(nums[list_idx]):
            break  # one list exhausted
        next_val = nums[list_idx][elem_idx + 1]
        heapq.heappush(heap, (next_val, list_idx, elem_idx + 1))
        current_max = max(current_max, next_val)
    return best

print(smallest_range([[4,10,15,24,26],[0,9,12,20],[5,18,22,30]]))
# [20, 24]

Elemen ke-K Terkecil dalam Matriks

Elemen Terkecil ke-K dalam Matriks Terurut (LeetCode #378): matriks n×n yang setiap baris dan kolomnya terurut. Temukan elemen terkecil ke-k. Perlakukan setiap baris sebagai list terurut dan gunakan penggabungan k-arah dengan tumpukan. Sebagai alternatif, gunakan pencarian biner pada rentang nilai. Pendekatan tumpukan memiliki kompleksitas O(k log n), sehingga efisien ketika k kecil; pencarian biner memiliki kompleksitas O(n log(max-min)), sehingga lebih baik untuk k yang besar.

import heapq

def kth_smallest_matrix(matrix, k):
    n = len(matrix)
    heap = [(matrix[0][0], 0, 0)]
    count = 0
    visited = {(0, 0)}
    while heap:
        val, r, c = heapq.heappop(heap)
        count += 1
        if count == k:
            return val
        # Push right neighbor
        if c + 1 < n and (r, c+1) not in visited:
            heapq.heappush(heap, (matrix[r][c+1], r, c+1))
            visited.add((r, c+1))
        # Push bottom neighbor
        if r + 1 < n and (r+1, c) not in visited:
            heapq.heappush(heap, (matrix[r+1][c], r+1, c))
            visited.add((r+1, c))
    return -1

matrix = [[1,5,9],[10,11,13],[12,13,15]]
print(kth_smallest_matrix(matrix, 8))  # 13

Dua Tumpukan untuk Statistik Berjalan

Pola dua tumpukan dapat diperluas melampaui median. Anda dapat menggunakannya untuk mempertahankan kuantil berjalan, misalnya persentil ke-25: atur ukuran tumpukan bawah agar menampung p*n elemen dan tumpukan atas agar menampung (1-p)*n elemen. Setiap kali elemen ditambahkan, seimbangkan kembali seperti sebelumnya. Pola ini muncul dalam masalah statistik aliran data yang memerlukan penyisipan efisien dan kueri kuantil secara bersamaan.

import heapq

# Generalised two-heap for arbitrary quantile p
# lo contains floor(p * count) elements
# hi contains the remaining elements
class QuantileFinder:
    def __init__(self, p):
        self.p = p  # quantile (e.g., 0.5 for median)
        self.lo = []  # max-heap
        self.hi = []  # min-heap
        self.count = 0

    def add(self, num):
        self.count += 1
        heapq.heappush(self.lo, -num)
        heapq.heappush(self.hi, -heapq.heappop(self.lo))
        # Target: lo should have floor(p * count) elements
        target_lo = int(self.p * self.count)
        while len(self.lo) < target_lo:
            heapq.heappush(self.lo, -heapq.heappop(self.hi))
        while len(self.lo) > target_lo:
            heapq.heappush(self.hi, -heapq.heappop(self.lo))

    def quantile(self):
        return -self.lo[0] if self.lo else self.hi[0]

qf = QuantileFinder(0.5)  # median
for n in [1, 2, 3, 4, 5, 6]: qf.add(n)
print(qf.quantile())  # 3 (median of 1-6)

Menemukan K Titik Terdekat dari Titik Asal

Titik Terdekat Sejumlah K dari Titik Asal (LeetCode #973) menggunakan tumpukan maksimum berukuran k. Lakukan push terhadap jarak kuadrat setiap titik untuk menghindari akar kuadrat. Saat ukuran tumpukan melebihi k, lakukan pop terhadap titik yang paling jauh. K titik yang tersisa adalah k titik terdekat. Kompleksitasnya O(n log k). Alternatifnya adalah menggunakan seleksi cepat dengan rata-rata O(n), tetapi solusi dengan tumpukan lebih sederhana untuk diimplementasikan dan dijelaskan dengan benar selama wawancara.

import heapq

def k_closest(points, k):
    heap = []  # max-heap via negation
    for x, y in points:
        dist_sq = x*x + y*y
        heapq.heappush(heap, (-dist_sq, x, y))
        if len(heap) > k:
            heapq.heappop(heap)  # remove farthest
    return [[x, y] for _, x, y in heap]

points = [[1,3], [-2,2], [5,8], [0,1], [-1,-1]]
print(k_closest(points, 2))
# Two closest to origin: [0,1] (dist=1) and [-1,-1] (dist=2)

# Verify by distances:
for x, y in points:
    print(f'({x},{y}): dist^2 = {x*x+y*y}')

Analisis Waktu dan Ruang Dua Tumpukan

Pendekatan dua tumpukan untuk median mencapai penyisipan O(log n) per addNum dan findMedian O(1). Ruang yang digunakan adalah O(n) untuk menyimpan semua elemen. Penggabungan k-arah memerlukan waktu O(n log k) dan ruang O(k) untuk tumpukan. Kompleksitas ini mendekati optimal: Anda dapat membuktikan batas bawah berbasis perbandingan sebesar Omega(n log k) untuk penggabungan k-arah, yang menunjukkan bahwa solusi tumpukan optimal secara asimtotik. Selalu nyatakan kompleksitas ini dengan jelas dalam wawancara.

# Complexity summary for heap applications:
# Problem               | Time per op  | Space
# ----------------------|--------------|------
# MedianFinder.addNum   | O(log n)     | O(n)
# MedianFinder.find     | O(1)         | -
# Merge k sorted lists  | O(n log k)   | O(k)
# Kth smallest matrix   | O(k log n)   | O(n)
# K closest points      | O(n log k)   | O(k)
# Task scheduler        | O(n log 26)  | O(26)
# Kth largest stream    | O(log k)     | O(k)
# Sliding window median | O(n log k)   | O(k)

print('Heap problems: identify k (heap size) vs n (input size)')

Pemeriksaan Singkat

Uji pemahaman Anda tentang konsep Struktur Data & Algoritma — Persiapan Wawancara Pemrograman dari pelajaran ini.

Rangkuman Pelajaran

Dalam pelajaran ini Anda mempelajari: MedianFinder dengan dua tumpukan yang mencapai penyisipan O(log n) dan median O(1), penggabungan k-arah dengan tumpukan minimum dalam waktu O(n log k) dan ruang O(k), serta perluasannya, termasuk median jendela geser, rentang terkecil, dan titik terdekat sejumlah k. Selanjutnya, kita akan membahas representasi graf dan persiapan penelusuran.

Pertanyaan yang Sering Diajukan

Apakah pelajaran “Median dari Aliran Data dan Penggabungan K Arah” gratis?

Ya — teks lengkap “Median dari Aliran Data dan Penggabungan K Arah” gratis dibaca di sini di web. Untuk praktiknya secara interaktif (editor kode bawaan dan tutor AI 24/7) dan buka sisa kursus DSA Interview Prep, upgrade ke CoddyKit PRO. Kursus DSA Interview Prep mencakup 4 pelajaran total.

Apa yang akan aku pelajari di “Median dari Aliran Data dan Penggabungan K Arah”?

Pertahankan dua heap (max-heap untuk separuh kecil dan min-heap untuk separuh besar) untuk pembaruan median O(log n), lalu gabungkan k list terurut menggunakan heap. Kamu berlatih DSA Interview Prep dengan kode praktik yang langsung kamu jalankan di browser, dan tutor AI 24/7 menjawab pertanyaanmu saat kamu mengerjakan pelajaran ini.

Apakah aku perlu pengalaman untuk memulai DSA Interview Prep?

Tidak diperlukan pengalaman sebelumnya. DSA Interview Prep di CoddyKit dirancang untuk pemula hingga pelajar tingkat lanjut, jadi kamu bisa memulai di sini atau dari awal dan belajar sesuai kecepatan kamu sendiri. Ini adalah pelajaran 4 dari 4.

Berapa lama pelajaran “Median dari Aliran Data dan Penggabungan K Arah” memakan waktu?

Sebagian besar pelajaran CoddyKit memakan waktu sekitar 5–10 menit. Setiap pelajaran ringkas dan interaktif, jadi kamu membuat kemajuan stabil dan melanjutkan dari tempat kamu tinggalkan di web dan aplikasi.

Bisakah aku menulis dan menjalankan kode dalam pelajaran DSA Interview Prep ini?

Ya. Setiap pelajaran DSA Interview Prep menyertakan editor kode bawaan, jadi kamu menulis dan menjalankan kode nyata langsung di browser dan mendapatkan umpan balik AI instan — tidak diperlukan penyiapan lokal.

Semua pelajaran dalam kursus ini

  1. Sifat Heap dan Representasi Array
  2. Heapify, Push, dan Pop dari Awal
  3. heapq Python dan Trik Max-Heap
  4. Median dari Aliran Data dan Penggabungan K Arah
← Kembali ke DSA Interview Prep