Priority Queue

堆Heap,一堆苹果,为了卖相好,越好看的越往上放,就是大顶堆;为了苹果堆的稳定,质量越小越往上放,就是小顶堆;堆首先是完全二叉树,但只确保父节点和子节点大小逻辑,不关心左右子节点的大小关系,通常是一个可以被看做一棵树的数组对象,是个很常见的结构,比如BST对象,都与堆有关系。

arbitrary element insertion but supports removal of elements in order of priority

A priority queue is an abstract data type for storing a collection of prioritized elements that supports arbitrary element insertion but supports removal of elements in order of priority, that is, the element with first priority can be removed at any time. This ADT is fundamentally different from the position-based data structures such as stacks, queues, deques, lists, and even trees, we discussed in previous chapters. These other data structures store elements at specific positions, which are often positions in a linear arrangement of the elements determined by the insertion and deletion operations performed. The priority queue ADT stores elements according to their priorities, and has no external notion of “position.”

Keys, Priorities, and Total Order Relations

Applications commonly require comparing and ranking objects according to parameters or properties, called “keys,” that are assigned to each object in a collection. Formally, we define a key to be an object that is assigned to an element as a specific attribute for that element and that can be used to identify, rank, or weigh that element. Note that the key is assigned to an element, typically by a user or application; hence, a key might represent a property that an element did not originally possess.

The key an application assigns to an element is not necessarily unique, however, and an application may even change an element’s key if it needs to. For example, we can compare companies by earnings or by number of employees; hence, either of these parameters can be used as a key for a company, depending on the information we wish to extract. Likewise, we can compare restaurants by a critic’s food quality rating or by average entrée price. To achieve the most generality then, we allow a key to be of any type that is appropriate for a particular application.

As in the examples above, the key used for comparisons is often more than a single numerical value, such as price, length, weight, or speed. That is, a key can sometimes be a more complex property that cannot be quantified with a single number. For example, the priority of standby passengers is usually determined by taking into account a host of different factors, including frequent-flyer status, the fare paid, and check-in time. In some applications, the key for an object is data extracted from the object itself (for example, it might be a member variable storing the list price of a book, or the weight of a car). In other applications, the key is not part of the object but is externally generated by the application (for example, the quality rating given to a stock by a financial analyst, or the priority assigned to a standby passenger by a gate agent).

A priority queue needs a comparison rule that never contradicts itself. In order for a comparison rule, which we denote by , to be robust in this way, it must define a total order relation, which is to say that the comparison rule is defined for every pair of keys and it must satisfy the following properties:
• Reflexive property : k ≤ k
• Antisymmetric property: if k1 ≤ k2 and k2 ≤ k1, then k1 = k2
• Transitive property: if k1 ≤ k2 and k2 ≤ k3, then k1 ≤ k3

在这里插入图片描述

The Priority Queue ADT

在这里插入图片描述

Sorting with a Priority Queue

Another important application of a priority queue is sorting, where we are given a collection L of n elements that can be compared according to a total order relation, and we want to rearrange them in increasing order (or at least in nondecreasing order if there are ties). The algorithm for sorting L with a priority queue Q, called PriorityQueueSort, is quite simple and consists of the following two phases:

  1. In the first phase, we put the elements of L into an initially empty priority queue P through a series of n insert operations, one for each element.
  2. In the second phase, we extract the elements from P in nondecreasing order by means of a series of n combinations of min and removeMin operations, putting them back into L in order.

在这里插入图片描述
The algorithm works correctly for any priority queue P, no matter how P is implemented. However, the running time of the algorithm is determined by the running times of operations insert, min, and removeMin, which do depend on how P is implemented. Indeed, PriorityQueueSort should be considered more a sorting “scheme” than a sorting “algorithm,” because it does not specify how the priority queue P is implemented.

The PriorityQueueSort scheme is the paradigm of several popular sorting algorithms, including selection-sort, insertion-sort, and heap-sort, which we discuss in this chapter.

The STL priority_queue Class

The priority queue class is templated with three parameters: the base type of the elements, the underlying STL container in which the priority queue is stored, and the comparator object. Only the first template argument is required. The second parameter (the underlying container) defaults to the STL vector. The third parameter (the comparator) defaults to using the standard C++ less-than operator (“<”). The STL priority queue uses comparators in the same manner as we defined in Section 8.1.2. In particular, a comparator is a class that overrides the “()” operator in order to define a boolean function that implements the less-than operator.

#include <queue>
using namespace std; // make std accessible

// a priority queue of integers
priority_queue<int> p1;

// a priority queue of points with left-to-right order
priority_queue<Point2D, vector<Point2D>, LeftRight> p2;
p2.push( Point2D(8.5, 4.6) ); // add three points to p2
p2.push( Point2D(1.3, 5.7) );
p2.push( Point2D(2.5, 0.6) );
cout << p2.top() << endl; p2.pop(); // output: (8.5, 4.6)
cout << p2.top() << endl; p2.pop(); // output: (2.5, 0.6)
cout << p2.top() << endl; p2.pop(); // output: (1.3, 5.7)

Implementation with an Unsorted List

在这里插入图片描述
在这里插入图片描述

Implementation with a Sorted List

在这里插入图片描述

Selection-Sort

If we implement the priority queue P with an unsorted list, then the first phase of PriorityQueueSort takes O(n) time, since we can insert each element in constant time. In the second phase, the running time of each min and removeMin operation is proportional to the number of elements currently in P. Thus, the bottleneck computation in this implementation is the repeated “selection” of the minimum element from an unsorted list in the second phase. For this reason, this algorithm is better known as selection-sort.
在这里插入图片描述
在这里插入图片描述

Insertion-Sort

If we implement the priority queue P using a sorted list, then we improve the running time of the second phase to O(n), because each operation min and removeMin on P now takes O(1) time. Unfortunately, the first phase now becomes the bottleneck for the running time, since, in the worst case, each insert operation takes time proportional to the size of P. This sorting algorithm is therefore better known as insertion-sort (see Figure 8.2), for the bottleneck in this sorting algorithm involves the repeated “insertion” of a new element at the appropriate position in a sorted list.

在这里插入图片描述
在这里插入图片描述

Heaps

在这里插入图片描述

The two implementations of the PriorityQueueSort scheme presented in the previous section suggest a possible way of improving the running time for priority-queue sorting. One algorithm (selection-sort) achieves a fast running time for the first phase, but has a slow second phase, whereas the other algorithm (insertion-sort) has a slow first phase, but achieves a fast running time for the second phase. If we could somehow balance the running times of the two phases, we might be able to significantly speed up the overall running time for sorting. This approach is, in fact, exactly what we can achieve using the priority-queue implementation discussed in this section.

An efficient realization of a priority queue uses a data structure called a heap. This data structure allows us to perform both insertions and removals in logarithmic time, which is a significant improvement over the list-based implementations discussed in Section 8.2. The fundamental way the heap achieves this improvement is to abandon the idea of storing elements and keys in a list and take the approach of storing elements and keys in a binary tree instead.

The Heap Data Structure

在这里插入图片描述
A heap is a binary tree T that stores a collection of elements with their associated keys at its nodes and that satisfies two additional properties:

  1. a relational property, defined in terms of the way keys are stored in T, and
  2. a structural property, defined in terms of the nodes of T itself.
    We assume that a total order relation on the keys is given, for example, by a comparator.
Heap-Order Property

在这里插入图片描述

Complete Binary Tree Property

在这里插入图片描述
在这里插入图片描述

The Height of a Heap

在这里插入图片描述
在这里插入图片描述
Proposition 8.5 has an important consequence. It implies that if we can perform update operations on a heap in time proportional to its height, then those operations will run in logarithmic time.

Therefore, let us turn to the problem of how to efficiently perform various priority queue functions using a heap.

Complete Binary Trees and Their Representation

在这里插入图片描述
在这里插入图片描述
The simplifications that come from representing a complete binary tree T with a vector aid in the implementation of functions add and remove. Assuming that no array expansion is necessary, functions add and remove can be performed in O(1) time because they simply involve adding or removing the last element of the vector. Moreover, the vector associated with T has n + 1 elements (the element at index 0 is a placeholder). If we use an extendable array that grows and shrinks for the implementation of the vector (for example, the STL vector class), the space used by the vector-based representation of a complete binary tree with n nodes is O(n) and operations add and remove take O(1) amortized time.

Implementing a Priority Queue with a Heap

在这里插入图片描述

insert

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

remove

在这里插入图片描述

Down-Heap Bubbling after a Removal

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

C++ 实现

A heap-based implementation of a priority queue.

We present an implementation of the insert operation. As outlined in the previous section, this works by adding the new element to the last position of the tree and then it performs up-heap bubbling by repeatedly swapping this element with its parent until its parent has a smaller key value.

Finally, let us consider the removeMin operation. If the tree has only one node, then we simply remove it. Otherwise, we swap the root’s element with the last element of the tree and remove the last element. We then apply down-heap bubbling to the root. Letting u denote the current node, this involves determining u’s smaller child, which is stored in v. If the child’s key is smaller than u’s, we swap u’s contents with this child’s.

template<typename E, typename C>
class HeapPriorityQueue {
public:
    int size() const; // number of elements
    bool empty() const; // is the queue empty?
    void insert(const E &e); // insert element
    const E &min(); // minimum element
    void removeMin(); // remove minimum
private:
    VectorCompleteTree <E> T; // priority queue contents
    C isLess; // less-than comparator

    // shortcut for tree position
    typedef typename VectorCompleteTree<E>::Position Position;
};

// number of elements
template<typename E, typename C>
int HeapPriorityQueue<E, C>::size() const { return T.size(); }

// is the queue empty?
template<typename E, typename C>
bool HeapPriorityQueue<E, C>::empty() const { return size() == 0; }

// minimum element
template<typename E, typename C>
const E &HeapPriorityQueue<E, C>::min() { return *(T.root()); }

// insert element
template<typename E, typename C>
void HeapPriorityQueue<E, C>::insert(const E &e) {
    T.addLast(e); // add e to heap
    Position v = T.last(); // e’s position

    while (!T.isRoot(v)) { // up-heap bubbling
        Position u = T.parent(v);
        if (!isLess(*v, *u)) {
            break; // if v in order, we’re done
        }
        T.swap(v, u); // else swap with parent
        v = u;
    }
}

// remove minimum
template<typename E, typename C>
void HeapPriorityQueue<E, C>::removeMin() {
    if (size() == 1) {// only one node?
        T.removeLast(); // remove it
    } else {
        Position u = T.root(); // root position
        T.swap(u, T.last()); // swap last with root
        T.removeLast(); // . . .and remove last

        while (T.hasLeft(u)) { // down-heap bubbling
            Position v = T.left(u);
            if (T.hasRight(u) && isLess(*(T.right(u)), *v)) {
                v = T.right(u); // v is u’s smaller child
            }
            if (isLess(*v, *u)) { // is u out of order?
                T.swap(u, v); // . . .then swap
                u = v;
            } else {
                break; // else we’re done
            }
        }
    }
}

Heap-Sort

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值