Close Menu
    Ciekawe

    Opera Portable – przeglądarka internetowa na pendrive bez instalacji

    2026-03-09

    doPDF – wirtualna drukarka do konwersji dokumentów na PDF

    2026-03-08

    Kaspersky Free – podstawowa ochrona antywirusowa za darmo

    2026-03-07
    Facebook X (Twitter) Instagram
    CPP Polska
    Facebook X (Twitter) Instagram
    • Biznes

      Programy VPN – ranking, porównanie i poradnik wyboru (2026)

      2026-02-26

      Obrót, przychód i dochód firmy – czym się różnią i jak je obliczyć?

      2026-02-16

      Restrukturyzacja i upadłość firmy – na czym polegają i jakie są konsekwencje?

      2026-02-14

      Składki ZUS dla firmy jednoosobowej w 2025 roku – ile wynoszą i jak je obliczyć?

      2026-01-28

      Co powinna zawierać pieczątka firmy jednoosobowej? Wymogi prawne i wzór

      2025-12-28
    • Technologie

      Opera Portable – przeglądarka internetowa na pendrive bez instalacji

      2026-03-09

      doPDF – wirtualna drukarka do konwersji dokumentów na PDF

      2026-03-08

      Kaspersky Free – podstawowa ochrona antywirusowa za darmo

      2026-03-07

      PeaZip – darmowy program do otwierania archiwów ZIP i RAR

      2026-03-05

      Jak oglądać filmy VR na komputerze? Wymagania i instrukcja

      2026-03-04
    • Programowanie

      Maszyna stanów oparta o std::variant

      2025-10-07

      std::deque w C++ – kiedy wybrać dwukierunkową kolejkę zamiast vectora

      2025-10-07

      Tablice w C++ od podstaw – deklaracja, inicjalizacja, iteracja i typowe pułapki

      2025-10-07

      itoa i std::to_chars – konwersja liczb na tekst bez narzutu wydajności

      2025-10-07

      strcpy vs strncpy vs std::string – bezpieczne kopiowanie łańcuchów w C++

      2025-10-07
    • Inne

      eSIM w Mobile Vikings – jak wirtualna karta SIM daje Ci wolność bez plastiku, kuriera i wychodzenia z domu

      2025-12-16

      Jak prowadzić blog programistyczny i dzielić się wiedzą?

      2025-06-28
    • Programy VPN – ranking
    CPP Polska
    Home»C++»std::priority_queue – implementacja kopca, własne komparatory i zastosowania algorytmiczne
    C++

    std::priority_queue – implementacja kopca, własne komparatory i zastosowania algorytmiczne

    Oskar KlimkiewiczBy Oskar KlimkiewiczBrak komentarzy5 Mins Read
    Share Facebook Twitter LinkedIn Email Copy Link
    Follow Us
    RSS
    a computer screen with a lot of text on it
    Share
    Facebook Twitter LinkedIn Email Copy Link

    # Implementacja std::priority_queue, własne komparatory i zastosowania algorytmiczne

    std::priority_queue to adapter kontenera w bibliotece standardowej C++, implementujący strukturę danych kolejki priorytetowej, oparty na kopcu binarnym. Umożliwia efektywne zarządzanie elementami według priorytetu, co jest kluczowe w wielu algorytmach. W niniejszym artykule szczegółowo omówimy działanie tej struktury, mechanizmy dostosowywania priorytetów oraz praktyczne zastosowania w algorytmice.

    Wprowadzenie do std::priority_queue

    std::priority_queue jest adapterem kontenera, który wykorzystuje kopiec binarny (zwykle na podstawie std::vector), aby zapewnić stały czas dostępu do elementu o najwyższym priorytecie (domyślnie maksymalnym). Operacje wstawiania (push) i usuwania (pop) działają w czasie logarytmicznym (O(\log n)). Domyślnie elementy są porządkowane z użyciem komparatora std::less, co oznacza, że top() zwraca największy element. Możliwość zmiany tego zachowania poprzez własne komparatory jest jedną z głównych cech tej struktury.

    Mechanizm działania kopca binarnego

    Implementacja std::priority_queue opiera się na własnościach kopca binarnego:

    1. Struktura drzewiasta – kopiec jest drzewem binarnym, gdzie wartość każdego węzła jest większa lub równa wartościom swoich dzieci (właściwość kopca maksymalnego);
    2. Przechowywanie w tablicy – drzewo kopca jest przechowywane w tablicy (najczęściej std::vector), gdzie:
    • indeks rodzica: (parent(i) = ⌊(i-1)/2⌋),
    • indeks lewego dziecka: (2i + 1),
    • indeks prawego dziecka: (2i + 2).
    1. Operacje –
    • push – element jest dodawany na koniec tablicy, następnie „wynoszony” do góry (ang. sift up), aż przywrócona zostanie właściwość kopca;
    • pop – element top() jest usuwany, a na jego miejsce wstawiany jest ostatni element tablicy, który jest „opuszczany” w dół (ang. sift down).

    Przykład operacji push:

    std::vector heap = {9, 8, 6, 7, 5, 2, 0};
    heap.push_back(4);
    std::push_heap(heap.begin(), heap.end()); // Wynik: {9, 8, 6, 7, 5, 2, 0, 4}
    

    Definiowanie własnych komparatorów

    Komparator decyduje o kolejności elementów w kolejce. Może to być:

    1. Funkcja lub funktor – klasa z operatorem ();
    struct Compare {
      bool operator()(const Foo& a, const Foo& b) {
        return a.priority > b.priority; // Kolejka minimalna
      }
    };
    

    Zastosowanie w priority_queue:

    std::priority_queue<Foo, std::vector<Foo>, Compare> pq;
    
    1. Wyrażenie lambda (C++11+) –
    auto comp = [](int a, int b) { return a > b; };
    std::priority_queue<int, std::vector<int>, decltype(comp)> pq(comp);
    
    1. Dla typów wbudowanych – domyślnie std::less (kolejka maksymalna), kolejka minimalna: std::priority_queue<int, std::vector<int>, std::greater<int>>.

    Przykład dla struktury Person:

    struct Person {
      int age;
      std::string name;
    };
    auto comp = [](Person a, Person b) { return a.age < b.age; }; // Najstarszy na górze
    std::priority_queue<Person, std::vector<Person>, decltype(comp)> pq(comp);
    

    Dynamiczna aktualizacja priorytetów – ograniczenia i rozwiązania

    std::priority_queue nie wspiera aktualizacji priorytetów dla elementów już umieszczonych w kolejce. Wynika to z kilku powodów:

    • brak powiadomień – kontener nie „wie”, że wartość elementu uległa zmianie,
    • kopiec nie jest automatycznie przebudowywany – po modyfikacji elementu konieczne jest przebudowanie kopca (O(n)).

    Rozwiązania:

    1. Wskaźniki – przechowywanie wskaźników do obiektów zamiast kopii;
    2. Algorytmy z ponownym wstawianiem – na przykład w algorytmie Dijkstry, po aktualizacji odległości wierzchołek jest ponownie wstawiany do kolejki;
    3. Rozszerzone struktury danych – __gnu_pbds::priority_queue (GCC) obsługuje modify oraz erase.

    Zastosowania algorytmiczne

    Algorytm Dijkstry (najkrótsze ścieżki)

    Kolejka priorytetowa minimalizuje czas wyboru wierzchołka o najmniejszej odległości:

    std::priority_queue<Node, std::vector<Node>, Compare> pq;
    pq.push({0, start});
    while (!pq.empty()) {
        Node u = pq.top(); pq.pop();
        for (auto [v, weight] : adj[u])
            if (dist[u] + weight < dist[v]) {
                dist[v] = dist[u] + weight;
                pq.push({dist[v], v}); // Ponowne wstawienie
            }
    }
    

    Złożoność: O((E + V) log V).

    Algorytm Prima (minimalne drzewo rozpinające)

    std::priority_queue<Edge, std::vector<Edge>, Compare> pq;
    pq.push({0, start});
    while (!pq.empty()) {
        auto [w, u] = pq.top(); pq.pop();
        for (auto [v, weight] : adj[u])
            if (!inMST[v] && weight < key[v]) {
                key[v] = weight;
                pq.push({key[v], v});
            }
    }
    

    Złożoność: O(E log V).

    Kodowanie Huffmana

    Budowa drzewa Huffmana wykorzystuje kolejkę priorytetową do łączenia węzłów o najmniejszej częstotliwości:

    std::priority_queue<HNode*, std::vector<HNode*>, Compare> pq;
    for (auto node : nodes) pq.push(node);
    while (pq.size() > 1) {
        auto left = pq.top(); pq.pop();
        auto right = pq.top(); pq.pop();
        auto parent = new HNode(left->freq + right->freq);
        pq.push(parent);
    }
    

    Uwaga: wymaga komparatora dla wskaźników.

    Scalanie K posortowanych list

    auto comp = [](ListNode* a, ListNode* b) { return a->val > b->val; };
    std::priority_queue<ListNode*, std::vector<ListNode*>, decltype(comp)> pq(comp);
    for (auto list : lists)
        if (list) pq.push(list);
    while (!pq.empty()) {
        ListNode* node = pq.top(); pq.pop();
        if (node->next) pq.push(node->next);
    }
    

    Złożoność: O(N log K), gdzie N to łączna liczba elementów.

    Ograniczenia std::priority_queue

    1. Brak dostępu do elementów – dostępny jest tylko element top();
    2. Brak iteratorów – niemożliwe jest przeglądanie elementów kolejki;
    3. Aktualizacja priorytetów – wymaga stosowania obejść (np. wskaźniki);
    4. Wydajność – push i pop: O(log n), tworzenie kopca: O(n), brak optymalnej lokalności w cache dla dużych struktur.

    Alternatywy i rozszerzenia

    1. std::set – umożliwia aktualizację (usunięcie i ponowne wstawienie), ale operacje w czasie O(log n) i wyższe zużycie pamięci;
    2. boost::heap – pairing_heap: obsługuje update oraz decrease_key w czasie O(log n);
    3. __gnu_pbds::priority_queue (GCC) –
    __gnu_pbds::priority_queue<T, Compare, Tag> pq;
    auto it = pq.push(x);
    pq.modify(it, new_x); // Aktualizacja
    

    Podsumowanie

    std::priority_queue jest potężnym narzędziem do zarządzania danymi według priorytetów, szczególnie efektywnym w implementacji algorytmów grafowych oraz problemów optymalizacyjnych. Pomimo braku wsparcia dla aktualizacji elementów, jego prostota i wydajność są niezastąpione w wielu scenariuszach. Rozszerzenia biblioteczne (takie jak pb_ds dostępne w GCC) oraz kreatywne użycie wskaźników lub ponownego wstawiania pozwalają obejść główne ograniczenia. Kluczową zaletą jest elastyczność w definiowaniu priorytetów dzięki komparatorom, co umożliwia dostosowanie struktury do złożonych typów danych.

    Rekomendacje –

    • W algorytmach wymagających częstych aktualizacji priorytetów rozważ __gnu_pbds::priority_queue lub boost::heap;
    • Dla prostych scenariuszy std::priority_queue z własnymi komparatorami (lambda, funktory) jest optymalnym wyborem;
    • Unikaj przechowywania dużych obiektów bezpośrednio – zamiast tego używaj wskaźników.

    Polecane:

    • STL w C++ – szybki przegląd kontenerów, adapterów i algorytmów dla początkujących
    • std::stack w C++ – adapter stosu, implementacja LIFO i przydatne metody
    • Przewodnik po coroutines w C++
    • std::shared_ptr w praktyce – cykle referencji, weak_ptr i custom deleter
    • Erase–remove idiom w C++
    Share. Facebook Twitter LinkedIn Email Copy Link
    Oskar Klimkiewicz
    • Website

    Inżynier oprogramowania specjalizujący się w C++, absolwent Wydziału Elektroniki i Technik Informacyjnych Politechniki Warszawskiej. Od ponad 8 lat projektuje i rozwija systemy o wysokiej dostępności, głównie dla branży fintech i IoT. PS. Zdjęcie wyretuszowane przez AI :)

    Podobne artykuły

    Maszyna stanów oparta o std::variant

    8 Mins Read

    Tablice w C++ od podstaw – deklaracja, inicjalizacja, iteracja i typowe pułapki

    4 Mins Read

    std::deque w C++ – kiedy wybrać dwukierunkową kolejkę zamiast vectora

    4 Mins Read
    Leave A Reply Cancel Reply

    Oglądaj, słuchaj, ćwicz - zdobywaj nowe umiejętności online
    Nie przegap

    Opera Portable – przeglądarka internetowa na pendrive bez instalacji

    Oskar Klimkiewicz4 Mins Read

    W erze mobilności i pracy zdalnej Opera Portable staje się nieocenionym narzędziem dla użytkowników, którzy…

    doPDF – wirtualna drukarka do konwersji dokumentów na PDF

    2026-03-08

    Kaspersky Free – podstawowa ochrona antywirusowa za darmo

    2026-03-07

    PeaZip – darmowy program do otwierania archiwów ZIP i RAR

    2026-03-05
    Social media
    • Facebook
    • Twitter
    • LinkedIn
    O nas
    O nas

    CPP Polska to serwis internetowy poświęcony technologii, programowaniu, IT, biznesowi i finansom. Znajdziesz tu porady, wskazówki i instrukcje dla wszystkich czytelników IT & Tech & Biz.

    Facebook X (Twitter) LinkedIn RSS
    Najnowsze

    Opera Portable – przeglądarka internetowa na pendrive bez instalacji

    2026-03-09

    doPDF – wirtualna drukarka do konwersji dokumentów na PDF

    2026-03-08

    Kaspersky Free – podstawowa ochrona antywirusowa za darmo

    2026-03-07
    Popularne

    Skrajnie niepotrzebne, skrajne przypadki w C++

    2025-06-28

    Wyszukiwanie testów w Google Test – metody i narzędzia

    2025-06-28

    Czy C jest wolniejszy od C++? Zero-cost abstraction w praktyce

    2025-06-28
    © 2026 CPP Polska. Wszelkie prawa zastrzeżone.
    • Lista publikacji
    • Współpraca
    • Kontakt

    Type above and press Enter to search. Press Esc to cancel.