This paper introduces how to use heap (heap) algorithm in STL. The first contact with heap data structure is in the university data structure textbook, it is a complete binary tree. In STL, heap is the form of algorithm for us to use. It includes the following functions:
-
make_heap: Create a heap. O(N) based on the specified iterator interval and an optional comparison function
-
push_heap: Insert the last element of the specified interval into the heap. O(logN)
-
pop_heap: Pop up the top element of heap and place it at the end of the interval. O(logN)
-
sort_heap: A heap sorting algorithm, usually implemented by calling pop_heap repeatedly. N*O(logN)
C++11 has two new members:
-
is_heap: Determine whether a given interval is a heap. O(N)?
-
is_heap_until: Find the first place in the interval that does not satisfy the heap condition. O(N)
Because heap is provided as an algorithm, to use these api s, you need to include # include < algorithm >
Use of heap-related algorithms
make_heap
The heap created by make_heap in STL is Max heap by default, that is, each non-leaf node element is not less than its left and right child nodes (default use < as a comparison criterion). To change the heap building criteria, you can develop a comparison function yourself, as shown in the second version of make_heap declaration:
// 1 template< class RandomIt > void make_heap( RandomIt first, RandomIt last ); // 2 template< class RandomIt, class Compare > void make_heap( RandomIt first, RandomIt last, Compare comp );
Sample code:
Default make_heap
vector<int> vi{6, 1, 2, 5, 3, 4}; printContainer(vi, "vi: "); // vi: 6 1 2 5 3 4 make_heap(vi.begin(), vi.end()); printContainer(vi, "vi: "); // vi: 6 5 4 1 3 2
It should be noted that make_heap needs to use a random iterator to create heap.
make_heap specifying its own comparison function
vector<int> v2{6, 1, 2, 5, 3, 4}; printContainer(v2, "v2 before make_heap: "); make_heap(v2.begin(), v2.end(), greater<int>()); printContainer(v2, "v2 after make_heap: ");
Output:
v2 before make_heap: 6 1 2 5 3 4 v2 after make_heap: 1 3 2 5 6 4
Greater < int > () is used instead of the default less < int > () to create heap of type int. You can draw this heap in the order of hierarchical traversal, and you can see that it's just the opposite of the default, and it's a small top heap.
push_heap
// 1 template< class RandomIt > void push_heap( RandomIt first, RandomIt last ); // 2 template< class RandomIt, class Compare > void push_heap( RandomIt first, RandomIt last, Compare comp );
Similar to make_heap, push_heap has two versions, one of which specifies the heap comparison function and operates on an interval specified by a pair of iterators.
Sample code:
vector<int> v1{6, 1, 2, 5, 3, 4}; make_heap(v1.begin(), v1.end()); v1.push_back(200); printContainer(v1, "before push_heap: "); // before push_heap: 6 5 4 1 3 2 200 push_heap(v1.begin(), v1.end()); printContainer(v1, "after push_heap: "); // after push_heap: 200 5 6 1 3 2 4
make_heap is used to construct a heap. After adding elements at the end of the container, a new iterator interval is passed to push_heap, so that new tail elements are added to the heap.
Note: With push_heap(f, l), the caller needs to make sure that [f, l-1] is already a heap. push_heap(f, l) only inserts * (l-1) into the heap formed by the interval [f, l-1], and the time complexity is O(logN).
That is, the caller has to ensure, on entry, the elements in the range [begin, end] are a heap(according to the same sorting criterion).
What happens if you don't start with make_heap processing?
vector<int> v2{6, 1, 2, 5, 3, 4}; v2.push_back(200); printContainer(v2, "v2 before push_heap: "); push_heap(v2.begin(), v2.end()); printContainer(v2, "v2 after push_heap: ");
Output:
v2 before push_heap: 6 1 2 5 3 4 200 v2 after push_heap: 200 1 6 5 3 4 2
You can see that the result of calling push_heap directly is not a heap. The pop_heap mentioned below also has the same requirement.
pop_heap
// 1 template< class RandomIt > void pop_heap( RandomIt first, RandomIt last ); // 2 template< class RandomIt, class Compare > void pop_heap( RandomIt first, RandomIt last, Compare comp ); Swaps the value in the position first and the value in the position last-1 and makes the subrange [first, last-1) into a max heap. This has the effect of removing the first (largest) element from the heap defined by the range [first, last).
Its function is to exchange * first and * (last-1), and then build [first, last-1] into a max heap. That is to say, the maximum element on the top of the heap is exchanged to the end of the interval, and then the remaining interval except the tail element is readjusted into a heap.
It is important to note that the caller ensures that [first, last] is already a heap (using the same sort criteria) when calling pop_heap.
vector<int> v1{6, 1, 2, 5, 3, 4}; make_heap(v1.begin(), v1.end()); printContainer(v1, "after make_heap: "); pop_heap(v1.begin(), v1.end()); printContainer(v1, "after pop_heap: "); auto largest = v1.back(); psln(largest); v1.pop_back(); printContainer(v1, "delete largest: ");
Output:
after make_heap: 6 5 4 1 3 2 after pop_heap: 5 3 4 1 2 6 largest = 6 delete largest: 5 3 4 1 2
sort_heap
// 1 template< class RandomIt > void sort_heap( RandomIt first, RandomIt last ); // 2 template< class RandomIt, class Compare > void sort_heap( RandomIt first, RandomIt last, Compare comp );
sort_heap is a classical heap sorting algorithm. By popping up the top of the heap until the heap is empty, the elements popped up in turn form an ordered sequence. priority_queue in STL is implemented using this feature of heap.
Intervals processed with sort_heap(f, l) are no longer heaps because they are ordered.
vector<int> v1{6, 1, 2, 5, 3, 4}; printContainer(v1, "before sort_heap: "); make_heap(v1.begin(), v1.end()); sort_heap(v1.begin(), v1.end()); printContainer(v1, "after sort_heap: ");
Output:
before sort_heap: 6 1 2 5 3 4 after sort_heap: 1 2 3 4 5 6
Note: The caller still needs to ensure that the interval is already a heap.
is_heap
// (1) (since C++11) template< class RandomIt > bool is_heap( RandomIt first, RandomIt last ); // (2) (since C++11) template< class RandomIt, class Compare > bool is_heap( RandomIt first, RandomIt last, Compare comp );
Example:
vector<int> v1{6, 1, 2, 5, 3, 4}; psln(is_heap(v1.begin(), v1.end())); pln("after make_heap"); make_heap(v1.begin(), v1.end()); psln(is_heap(v1.begin(), v1.end()));
Output:
is_heap(v1.begin(), v1.end()) = 0 after make_heap is_heap(v1.begin(), v1.end()) = 1
is_heap_until
Archetype:
// (1) (since C++11) template< class RandomIt > RandomIt is_heap_until( RandomIt first, RandomIt last ); // (2) (since C++11) template< class RandomIt, class Compare > RandomIt is_heap_until( RandomIt first, RandomIt last, Compare comp );
vector<int> v1{6, 1, 2, 5, 3, 4}; auto iter = is_heap_until(v1.begin(), v1.end()); psln(*iter); // * ITER = 55 is the first location that does not satisfy the heap condition. make_heap(v1.begin(), v1.end()); iter = is_heap_until(v1.begin(), v1.end()); ASSERT_TRUE(iter == v1.end());
summary
-
Build a heap, make_heap
-
Heap operations: add elements (push_heap), delete elements (pop_heap), sort_heap, all require that the interval is already a heap, and use the same sort criteria as the current operation
-
Is_heap, is_heap_until is used as an auxiliary judgment function
-
All intervals of heap algorithm operations need to be composed of random iterators