Menu
Coddy logo textTech

Kopiec (kopiec binarny)

Ostatnia aktualizacja

Kopiec binarny to zupełne drzewo binarne, które trzyma najmniejszą (kopiec min) lub największą (kopiec max) wartość w korzeniu. Ta wizualizacja pokazuje kopiec min: każdy rodzic jest mniejszy lub równy swoim dzieciom. Aby dodać wartość, dopisujesz ją w następnym wolnym miejscu, a potem "przesiewasz ją w górę", zamieniając z rodzicem, dopóki jest od niego mniejsza, aż własność kopca znów będzie spełniona. Kliknij odtwarzanie powyżej i zobacz, jak każda nowa wartość wypływa na swoje miejsce.

Ponieważ kopiec jest drzewem zupełnym, przechowuje się go zwięźle w tablicy: dzieci węzła i są pod indeksami 2i+1 i 2i+2. Wstawianie i usuwanie minimum kosztują O(log n) (jedna ścieżka od korzenia do liścia), a podgląd minimum O(1), czyli dokładnie to, czego potrzebuje kolejka priorytetowa.

Złożoność czasowa i pamięciowa

OperacjaZłożonośćUwagi
Podgląd min/maxO(1)To zawsze korzeń
Wstawianie (push)O(log n)Przesiewanie w górę jednej ścieżki
Usuwanie min/maxO(log n)Przesiewanie w dół jednej ścieżki
Budowa kopcaO(n)Kopcowanie wszystkiego naraz
PamięćO(n)Oparty na tablicy, bez wskaźników

Krok po kroku (push)

KrokCo się dzieje
1Dopisz nową wartość na końcu (następny wolny liść).
2Porównaj ją z rodzicem.
3Jeśli jest mniejsza (kopiec min), zamień ją w górę.
4Powtarzaj, dopóki nie przestanie być mniejsza od rodzica albo nie dotrze do korzenia.

Przykład krok po kroku

Budowanie kopca min przez dodawanie [5, 3, 8, 1, 4] po jednej wartości:

PushTablica po przesianiu w góręDziałanie
5[5]Pierwsza wartość zostaje korzeniem.
3[3, 5]3 < rodzic 5, więc zamień ją w górę do korzenia.
8[3, 5, 8]8 > rodzic 5, więc zostaje liściem.
1[1, 3, 8, 5]1 < rodzic 5, zamiana; potem 1 < rodzic 3, zamiana aż do korzenia.
4[1, 3, 8, 5, 4]4 > rodzic 3, więc zostaje; minimum 1 pozostaje w korzeniu.

Kiedy używać kopca

Używaj, gdyUnikaj, gdy
Wielokrotnie potrzebujesz najmniejszego lub największego elementu ze zmieniającego się zbioru.Musisz wyszukiwać dowolne wartości, a nie tylko skrajne: użyj drzewa BST lub zbioru haszującego.
Implementujesz kolejkę priorytetową dla algorytmu Dijkstry, A* lub planisty zadań.Potrzebujesz danych przez cały czas w pełni posortowanych.
Chcesz wstawiania i usuwania minimum w O(log n) przy zwięzłym układzie w tablicy.Potrzebujesz szybkiego wyszukiwania lub usuwania konkretnego elementu (innego niż korzeń).
Musisz scalać strumień elementów i wyciągać je według priorytetu.Zbiór danych jest malutki, a liniowe przeglądanie jest prostsze i wystarczająco szybkie.

Heap (Priority Queue): kod

Przejrzysta, gotowa do uruchomienia implementacja algorytmu Heap (Priority Queue) w językach: Python, JavaScript, Java, C++, C. Wybierz język, skopiuj kod albo otwórz go od razu w edytorze online Coddy.

Heap (Priority Queue): kod (Python)

Python
1class MinHeap:2    def __init__(self):3        self.data = []4
5    def push(self, value):6        # Append at the end, then bubble up to restore order7        self.data.append(value)8        i = len(self.data) - 19        while i > 0:10            parent = (i - 1) // 211            if self.data[parent] <= self.data[i]:12                break13            self.data[i], self.data[parent] = self.data[parent], self.data[i]14            i = parent15
16    def pop(self):17        # Move the last leaf to the root, then sift it down18        top = self.data[0]19        last = self.data.pop()20        if self.data:21            self.data[0] = last22            self._sift_down(0)23        return top24
25    def _sift_down(self, i):26        n = len(self.data)27        while True:28            smallest = i29            left, right = 2 * i + 1, 2 * i + 230            if left < n and self.data[left] < self.data[smallest]:31                smallest = left32            if right < n and self.data[right] < self.data[smallest]:33                smallest = right34            if smallest == i:35                return36            self.data[i], self.data[smallest] = self.data[smallest], self.data[i]37            i = smallest38
39
40heap = MinHeap()41for value in [5, 3, 8, 1, 9, 2]:42    heap.push(value)43
44print("Heap array:     ", heap.data)45print("Popped in order:", [heap.pop() for _ in range(6)])
Uruchom ten kod w edytorze Python online

Kopiec: najczęstsze pytania

Do czego służy kopiec?
Kopce implementują kolejki priorytetowe, na których opiera się algorytm najkrótszej ścieżki Dijkstry, planiści zadań i symulacje zdarzeń. Są też silnikiem sortowania przez kopcowanie. Zawsze, gdy wielokrotnie potrzebujesz najmniejszego lub największego elementu ze zmieniającego się zbioru, kopiec jest właściwym narzędziem.
Czym różni się kopiec od binarnego drzewa poszukiwań?
Oba są drzewami binarnymi, ale drzewo BST utrzymuje pełny porządek od lewej do prawej (co umożliwia wyszukiwanie w porządku), a kopiec gwarantuje tylko relację rodzic-dziecko (min lub max w korzeniu). Kopiec daje dostęp do wartości skrajnej w O(1); drzewo BST daje wyszukiwanie dowolnej wartości w O(log n).
Dlaczego kopiec przechowuje się w tablicy?
Ponieważ kopiec jest zawsze zupełnym drzewem binarnym, jego węzły idealnie odwzorowują się na indeksy tablicy: dzieci indeksu i są pod 2i+1 i 2i+2, a rodzic pod (i-1)/2. Dzięki temu nie trzeba przechowywać wskaźników na dzieci, a wydajność pamięci podręcznej jest świetna.
Czy kopiec to to samo co posortowana tablica?
Nie. Kopiec gwarantuje tylko, że każdy rodzic jest mniejszy (kopiec min) lub większy (kopiec max) od swoich dzieci, więc rodzeństwo i kuzyni nie mają określonego porządku. Posortowana tablica jest w pełni uporządkowana, ale wstawianie do niej kosztuje O(n), a kopiec wstawia w O(log n) i nadal daje natychmiastowy dostęp do wartości skrajnej.
Kiedy użyć kopca zamiast po prostu posortować tablicę?
Sięgnij po kopiec, gdy dane ciągle się zmieniają, a potrzebujesz tylko bieżącego minimum lub maksimum: wstawienie i zdjęcie kosztują po O(log n), zamiast ponownego sortowania całej tablicy. Jeśli zbiór danych jest statyczny i chcesz mieć wszystkie elementy w kolejności, jedno sortowanie O(n log n) jest prostsze i często szybsze.
Czy zbudowanie kopca z n elementów zajmuje O(n log n)?
Nie, jeśli budujesz go naraz. Wstawianie n elementów po kolei kosztuje O(n log n), ale oddolne heapify, które przesiewa w dół od ostatniego rodzica do korzenia, działa łącznie w O(n), bo większość węzłów leży blisko dołu i przesiewa się tylko na krótką odległość.
Ilustracja języków programowania w Coddy

Opanuj algorytmy z Coddy

ZACZNIJ