The previous two articles introduced the source code implementation of vector and list in gcc4.8. This is the two most commonly used sequence containers in STL. In addition to containers, STL also provides a component called an adapter that uses containers for special operations, such as stack, queue, and priority queue, this article introduces the source code implementation of the priority queue of gcc4.8.
As the name suggests, priority queue is a queue with a priority. Therefore, elements must provide the <operator. Unlike vector and list, priority queue allows elements to be added, but only elements with the highest priority can be retrieved.
1. priority queue Definition
Priority queue does not have a base class
template<typename _Tp, typename _Sequence = vector<_Tp>, typename _Compare = less<typename _Sequence::value_type> >class priority_queue { public: typedef typename _Sequence::value_type value_type; typedef typename _Sequence::reference reference; typedef typename _Sequence::const_reference const_reference; typedef typename _Sequence::size_type size_type; typedef _Sequence container_type; protected: _Sequence c; _Compare comp;…...
Priority queue uses vector by default at the underlying layer, which contains two members. Vector C stores data. Comp is a function to compare the data size.
Ii. priority queue Construction Method
You can use vector to directly initialize priority queue, or any iterator or array pointer to initialize.
explicit priority_queue(const _Compare& __x, const _Sequence& __s) : c(__s), comp(__x) { std::make_heap(c.begin(), c.end(), comp); } explicit priority_queue(const _Compare& __x = _Compare(), _Sequence&& __s = _Sequence()) : c(std::move(__s)), comp(__x) { std::make_heap(c.begin(), c.end(), comp); } template<typename _InputIterator> priority_queue(_InputIterator __first, _InputIterator __last, const _Compare& __x, const _Sequence& __s) : c(__s), comp(__x) { __glibcxx_requires_valid_range(__first, __last); c.insert(c.end(), __first, __last); std::make_heap(c.begin(), c.end(), comp); } template<typename _InputIterator> priority_queue(_InputIterator __first, _InputIterator __last, const _Compare& __x = _Compare(), _Sequence&& __s = _Sequence()) : c(std::move(__s)), comp(__x) { __glibcxx_requires_valid_range(__first, __last); c.insert(c.end(), __first, __last); std::make_heap(c.begin(), c.end(), comp); }
Insert all elements into the priority queue and use make_heap to build the largest heap,
template<typename _RandomAccessIterator> void make_heap(_RandomAccessIterator __first, _RandomAccessIterator __last) { typedef typename iterator_traits<_RandomAccessIterator>::value_type _ValueType; typedef typename iterator_traits<_RandomAccessIterator>::difference_type _DistanceType; if (__last - __first < 2) return; const _DistanceType __len = __last - __first; _DistanceType __parent = (__len - 2) / 2; while (true) { _ValueType __value = _GLIBCXX_MOVE(*(__first + __parent)); std::__adjust_heap(__first, __parent, __len, _GLIBCXX_MOVE(__value)); if (__parent == 0) return; __parent--; } }
_ Adjust_heap is a tracing process. the tracing process is performed one by one from the last non-leaf node so that it is the largest heap for the subtree of the root node.
template<typename _RandomAccessIterator, typename _Distance, typename _Tp, typename _Compare> void __adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex, _Distance __len, _Tp __value, _Compare __comp) { const _Distance __topIndex = __holeIndex; _Distance __secondChild = __holeIndex; while (__secondChild < (__len - 1) / 2) { __secondChild = 2 * (__secondChild + 1); if (__comp(*(__first + __secondChild), *(__first + (__secondChild - 1)))) __secondChild--; *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __secondChild)); __holeIndex = __secondChild; } if ((__len & 1) == 0 && __secondChild == (__len - 2) / 2) { __secondChild = 2 * (__secondChild + 1); *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + (__secondChild - 1))); __holeIndex = __secondChild - 1; } std::__push_heap(__first, __holeIndex, __topIndex, _GLIBCXX_MOVE(__value), __comp); }
3. priority queue element operations
Priority queue only has two main operations: Push and pop. New elements are added to push,
void push(const value_type& __x) { c.push_back(__x); std::push_heap(c.begin(), c.end(), comp); }
Put it in the last position, and then use push_heap to perform an upstream operation to move the inserted element to the appropriate position to ensure that the entire queue is still the largest heap.
template<typename _RandomAccessIterator, typename _Distance, typename _Tp> void __push_heap(_RandomAccessIterator __first, _Distance __holeIndex, _Distance __topIndex, _Tp __value) { _Distance __parent = (__holeIndex - 1) / 2; while (__holeIndex > __topIndex && *(__first + __parent) < __value) { *(__first + __holeIndex) = _GLIBCXX_MOVE(*(__first + __parent)); __holeIndex = __parent; __parent = (__holeIndex - 1) / 2; } *(__first + __holeIndex) = _GLIBCXX_MOVE(__value); }
The pop operation removes the heap top element,
Void POP ()
{
STD: pop_heap (C. Begin (), C. End (), comp );
C. pop_back ();
}
Because vector is used, it will be very costly to remove the first element and then make_heap. Here, we will first swap the first element with the last element, delete the last element, and then perform a tracing process from the first element to create a new largest heap.