Fork me on GitHub

STL笔记之heap

1. STL笔记之heap

1.1. 简介

heap(堆)是一种比较复杂的数据结构。不过 STL 已经封装了 heap 的一些操作,比如:创建堆、判断堆、插入一个元素、删除一个元素、以及堆排序。在 STL 中,heap 是以算法的形式提供的,包括下面几个函数:

  • make_heap():根据指定的迭代器区间以及一个可选的比较函数(通常为 greater<T>() 或者 less<T>(),其中 T 为一种数据类型),来创建一个 heap,时间复杂度为 O(n);
  • push_heap():把指定区间的最后一个元素插入到 heap 中,时间复杂度为 O(logn);
  • pop_heap():弹出 heap 顶元素,将其放置于区间末尾,时间复杂度 O(logn);
  • sort_heap():堆排序算法,将指定区间的元素进行堆排序,时间复杂度为 O(nlogn)。

C++11 加入了两个新成员:

  • is_heap():判断给定区间是否是一个 heap,时间复杂度为 O(n);
  • is_heap_until():找出区间中第一个不满足 heap 条件的元素,返回指向该元素的指针。时间复杂度为 O(n)。

因为 heap 都是以算法的形式提供,所以使用这几个函数需要包含如下头文件:

1
#include <algorithm>

1.2. 相关算法的使用

使用数组来存储一个堆,也就是堆化数组:

1
2
3
int arr[] = {2, 14, 4, 16, 6, 5, 3};
vector<int> vec(arr, arr + sizeof(arr)/sizeof(int));
// vector<int> vec{2, 14, 4, 16, 6, 5, 3};

注:下述函数的使用都是代码片段,且使用的比较函数都是 greater<T>(),即小根堆,默认的是 less<T>(),即大根堆。完整代码在本文最后。

1. is_heap():判断堆

原型:

1
2
3
4
5
6
7
// 1
template< typename RandomIt >
bool is_heap( RandomIt first, RandomIt last );

// 2
template< typename RandomIt, typename Compare >
bool is_heap( RandomIt first, RandomIt last, Compare comp );

用法示例:

1
cout << is_heap(vec.begin(), vec.end()) << endl;

2. is_heap_until():找出区间中第一个不满足 heap 条件的元素,返回指向该元素的指针。

原型:

1
2
3
4
5
6
7
// (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 );

用法示例:

1
2
vector<int>::iterator iter = is_heap_until(vec.begin(), vec.end(), greater<int>());
cout << "is heap until: " << *iter << endl;

3. make_heap():构造堆

STL 中的通过 make_heap() 创建的堆,默认是大根堆(max heap)。要改变堆的建立准则,可以自己制定一个比较函数,比如如下第二个版本的第三个参数。第三个参数可填:less<int>() 构造大根堆,greater<int>() 构造小根堆。(注意:要使用其中一个比较函数,在该堆上使用的其他算法也要使用该比较函数)

原型:

1
2
3
4
5
6
7
// 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 );

用法示例:

1
make_heap(vec.begin(), vec.end(), greater<int>());

4. push_heap():插入元素

如果要在堆中插入一个元素,首先要往容器 vector 尾部插入一个元素,然后把新的迭代区间传给 push_heap(),这样新的元素也被插入到堆中。时间复杂度为 O(logn)。

原型:

1
2
3
4
5
6
7
// 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 );

用法示例:

1
2
vec.push_back(20);
push_heap(vec.begin(), vec.end(), greater<int>());

5. pop_heap():弹出元素

弹出的元素默认是根节点的元素。这个算法的作用是交换根结点元素与堆中的最后一个元素,然后把除了尾部元素的剩余区间重新调整成堆。注意,pop_heap() 函数并没有真正的删除元素,最后要使用容器的 pop_back() 来删除元素。

原型:

1
2
3
4
5
6
7
8
9
10
// 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).

用法示例:

1
2
pop_heap(vec.begin(), vec.end(), greater<int>());
vec.pop_back();

6. sort_heap():堆排序

该算法实现经典的堆排序算法,通过每次弹出堆顶元素直到堆为空,依次被弹出的元素就组成了有序的序列。STL 中的 priority_queue 既是用 heap 的这个特性来实现的。

注意:使用 sort_heap() 时要确保区间已经是一个堆了,处理过后的区间因为有序,就不再是一个 heap 了。

原型:

1
2
3
4
5
6
7
// 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 );

用法示例:

1
sort_heap(vec.begin(), vec.end(), greater<int>());

7. 完整代码示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <vector>
using namespace std;

template<typename T>
ostream& operator<<(ostream& out, vector<T>& vec){
for (int i = 0; i < vec.size(); ++i){
out << vec[i] << ' ';
}
return out;
}

int main(int argc, char **argv)
{
// int arr[] = {2, 14, 4, 16, 6, 5, 3};
// vector<int> vec(arr, arr + sizeof(arr)/sizeof(int));
vector<int> vec{2, 14, 4, 16, 6, 5, 3};
cout << "is heap: " << is_heap(vec.begin(), vec.end()) << endl;
make_heap(vec.begin(), vec.end(), greater<int>());
cout << "make heap: " << vec << endl;
vec.push_back(20);
push_heap(vec.begin(), vec.end(), greater<int>());
cout << "push heap: " << vec << endl;
pop_heap(vec.begin(), vec.end(), greater<int>());
vec.pop_back();
cout << "pop heap: " << vec << endl;
sort_heap(vec.begin(), vec.end(), greater<int>());
cout << "sort heap: " << vec << endl;
vector<int>::iterator iter = is_heap_until(vec.begin(), vec.end(), greater<int>());
cout << "is heap until: " << *iter << endl;
return 0;
}

代码输出为(注意,g++ 编译时需要添加 -std=c++11):

1
2
3
4
5
6
is heap: 0
make heap: 2 6 3 16 14 5 4
push heap: 2 6 3 16 14 5 4 20
pop heap: 3 6 4 16 14 5 20
sort heap: 20 16 14 6 5 4 3
is heap until: 16