Close Menu
    Ciekawe

    Virtual CloneDrive – montowanie obrazów ISO w wirtualnym napędzie

    2026-07-18

    Jak zabezpieczyć WordPressa? Wtyczki i dobre praktyki ochrony

    2026-07-17

    DeepBurner – nagrywanie płyt CD i DVD z danymi oraz audio

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

      Packshoty 3D wypierają tradycyjną fotografię produktową. Dlaczego wielkie marki rezygnują ze studiów fotograficznych?

      2026-06-24

      Jak skrócić czas mikroprzestojów na produkcji?  

      2026-06-16

      Gdzie uciekają pieniądze w Twoim obiegu dokumentów?  

      2026-06-16

      Ukryte koszty projektów – jak je zidentyfikować i ograniczyć?

      2026-05-22

      Jak sprawdzić pomysł na biznes? MVP a badania konsumenckie

      2026-05-13
    • Technologie

      Virtual CloneDrive – montowanie obrazów ISO w wirtualnym napędzie

      2026-07-18

      Jak zabezpieczyć WordPressa? Wtyczki i dobre praktyki ochrony

      2026-07-17

      DeepBurner – nagrywanie płyt CD i DVD z danymi oraz audio

      2026-07-15

      Tworzenie aplikacji webowych i mobilnych – metodyka i szacowanie kosztów

      2026-07-13

      LyX – procesor dokumentów oparty na systemie składu LaTeX

      2026-07-11
    • 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

      Bezpieczeństwo finansowe w sektorze IT

      2026-04-29

      Tłumaczenia symultaniczne – klucz do sprawnej komunikacji na międzynarodowych wydarzeniach

      2026-03-26

      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

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

    4 Mins Read

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

    4 Mins Read
    Leave A Reply Cancel Reply

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

    Virtual CloneDrive – montowanie obrazów ISO w wirtualnym napędzie

    Oskar Klimkiewicz5 Mins Read

    W erze, gdy fizyczne płyty CD i DVD stają się reliktem przeszłości, Virtual CloneDrive oferuje…

    Jak zabezpieczyć WordPressa? Wtyczki i dobre praktyki ochrony

    2026-07-17

    DeepBurner – nagrywanie płyt CD i DVD z danymi oraz audio

    2026-07-15

    Tworzenie aplikacji webowych i mobilnych – metodyka i szacowanie kosztów

    2026-07-13
    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

    Virtual CloneDrive – montowanie obrazów ISO w wirtualnym napędzie

    2026-07-18

    Jak zabezpieczyć WordPressa? Wtyczki i dobre praktyki ochrony

    2026-07-17

    DeepBurner – nagrywanie płyt CD i DVD z danymi oraz audio

    2026-07-15
    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.