AiSD (index)


AiSD (9) - ADT PRIORITY QUEUE

OBOWIĄZKOWE DO PRZESŁANIA: jedno zadanie

WPROWADZENIE

Kolejka priorytetowa to ADT zawierający elementy z kluczami (priorytetami), która pozwala na przeprowadzenie następujących operacji:

Kolejki priorytetowe często używają wewnętrznie kopca. Wtedy tablicę zamienia się w kopiec w czasie O(n), a wstawianie i usuwanie elementów zajmuje czas O(log n). Alternatywą są drzewa binarne samobalansujące się.


// https://en.cppreference.com/w/cpp/container/priority_queue

#include <queue>

// W STL kolejka priorytetowa jest zbudowana na bazie std::vector,
// inna możliwość to std::deque.
// Jest to otoczka do zarządzania wewnętrznym kopcem.
// Domyślnie w stałym czasie mamy dostęp do elementu największego.
// Wstawianie i usuwanie elementów z kolejki zajmuje czas O(log n).
// Można dostarczyć funkcję porównującą, która będzie użyta do ustalenia
// priorytetów elementów, domyślnie jest to std::less.
// Przykład użycia kolejki priorytetowej.

//std::priority_queue<int> pq;   // domyślnie std::vector
//std::priority_queue<int, std::deque<int> > pq;
//std::priority_queue<int, std::vector<int>, std::greater<int> > pq;

// Własna funkcja porównująca.
auto cmp = [] (int left, int right) { return left > right; };
std::priority_queue<int, std::vector<int>, decltype(cmp) > pq(cmp);

// https://en.cppreference.com/w/cpp/language/decltype
// decltype() is useful when declaring types that are difficult
// or impossible to declare using standard notation, like lambda-related
// types or types that depend on template parameters. 

// Wstawianie do kolejki par (priority, item) [jak w Pythonie].
auto cmp = [] (std::pair<int,float> left, std::pair<int,float> right) {
    return left.first < right.first; };
std::priority_queue< std::pair<int,float>,
    std::vector< std::pair<int,float> >,
    decltype(cmp) > pq(cmp);

assert( pq.empty() );
for (auto item : {1, 5, 4, 2, 3})
    pq.push(item);   // wstawienie do kolejki
assert( pq.size() == 5 );
// Pobranie z kolejki elementu i wypisanie go.
std::cout << pq.top() << std::endl;   // wypisuje, ale nie zdejmuje!
pq.pop();    // usuwa z kolejki, ale nie zwraca
assert( pq.size() == 4 );
while (!pq.empty()) {
    std::cout << pq.top() << std::endl;
    pq.pop();
}

// https://en.cppreference.com/w/cpp/algorithm/make_heap

// Constructs a max heap in the range [first, last).

template < class RandomIt >
void make_heap( RandomIt first, RandomIt last );

template < class RandomIt >
void make_heap( RandomIt first, RandomIt last, Compare comp );

// https://en.cppreference.com/w/cpp/algorithm/push_heap

// A new element can be added using std::push_heap().
// Element na pozycji last-1 jest wstawiany do kopca [first, last-1).

template < class RandomIt >
void push_heap( RandomIt first, RandomIt last );

template < class RandomIt >
void push_heap( RandomIt first, RandomIt last, Compare comp );

// https://en.cppreference.com/w/cpp/algorithm/pop_heap

// The first element can be removed using std::pop_heap().
// Element z pozycji first jest zamieniany z last-1, a następnie
// zakres [first, last-1) jest zamieniany na kopiec.

template < class RandomIt >
void pop_heap( RandomIt first, RandomIt last );

template < class RandomIt >
void pop_heap( RandomIt first, RandomIt last, Compare comp );

// https://en.cppreference.com/w/cpp/algorithm/sort_heap

// Converts the max heap [first, last) into a sorted range in ascending order.
// The resulting range no longer has the heap property. 

template < class RandomIt >
void sort_heap( RandomIt first, RandomIt last );

template < class RandomIt >
void sort_heap( RandomIt first, RandomIt last, Compare comp );

// Sortowanie przez kopcowanie, złożoność O(n log n).
std::vector<int> v = {3, 1, 4, 1, 5, 9};
std::make_heap(v.begin(), v.end()); // tworzenie kopca
std::sort_heap(v.begin(), v.end()); // wyjmowanie elementów największych

ZADANIE 9.1

Przygotować implementację realizującą ADT PRIORITY QUEUE na bazie std::vector i kopca z STL. Wydajność: wstawianie O(log n), usuwanie O(log n), odczyt największego O(1).


#include <vector>
#include <algorithm>   // make_heap, push_heap, pop_heap

template <typename T>
class MyPriorityQueue {
    std::vector<T> lst; // działa domyślny konstruktor dla std::vector
public:
    MyPriorityQueue(int s=10) { lst.reserve(s); } // default constructor
    ~MyPriorityQueue() { lst.clear(); }
    MyPriorityQueue(const MyPriorityQueue& other); // copy constructor
    MyPriorityQueue(MyPriorityQueue&& other); // move constructor
    MyPriorityQueue& operator=(const MyPriorityQueue& other); // copy assignment operator, return *this
    MyPriorityQueue& operator=(MyPriorityQueue&& other); // move assignment operator, return *this
    bool empty() const { return lst.empty(); }
    int size() const { return lst.size(); } // liczba elementów w kolejce
    void push(const T& item) { // dodanie do kolejki
        lst.push_back(item);
        std::push_heap(lst.begin(), lst.end());
    }
    void push(T&& item) { // dodanie do kolejki
        lst.push_back(std::move(item));
        std::push_heap(lst.begin(), lst.end());
    }
    T& top() { return lst.front(); } // zwraca element największy, nie usuwa
    void pop() { // usuwa element największy
        std::pop_heap(lst.begin(), lst.end());
        lst.pop_back();
    }
    void clear() { lst.clear(); } // czyszczenie listy z elementów
    void display();
};

ZADANIE 9.2

Przygotować implementację realizującą ADT PRIORITY QUEUE nie korzystającą z kopca z STL, tylko własnych funkcji. Wydajność: wstawianie O(log n), usuwanie O(log n), odczyt największego O(1).

ZADANIE 9.3

Przygotować prostą implementację realizującą ADT PRIORITY QUEUE na bazie listy powiązanej pojedynczo. Lista jest stale uporządkowana, a największy element znajduje się na początku listy. Można wykorzystać klasę SingleList lub std::forward_list. Wydajność: wstawianie O(n), usuwanie O(1), odczyt największego O(1).

ZADANIE 9.4

Przygotować prostą implementację realizującą ADT PRIORITY QUEUE na bazie tablicy. Tablica nie jest uporządkowana. Wstawiamy nowy element na koniec tablicy. Przy usuwaniu elementu największego znajdujemy jego indeks, zamieniamy go miejscami z ostatnim elementem i skracamy tablicę jak dla stosu. Wydajność: wstawianie O(1), usuwanie O(n), odczyt największego O(n).


AiSD (index)