目录
- 一、二叉堆的核心思想与本质
- 二、二叉堆的实现与操作
-
- [1. 存储结构](#1. 存储结构)
- [2. 基本操作](#2. 基本操作)
- 三、二叉堆的应用场景
-
- [1. 优先队列](#1. 优先队列)
- [2. Top-K 问题](#2. Top-K 问题)
- [3. 堆排序](#3. 堆排序)
- [4. 中位数维护](#4. 中位数维护)
- 四、二叉堆的复杂度分析
- 五、二叉堆的变种与高阶应用
-
- [1. 二项堆](#1. 二项堆)
- [2. 斐波那契堆](#2. 斐波那契堆)
- [3. 左偏堆](#3. 左偏堆)
- 六、常见陷阱与调试技巧
-
- [1. 边界条件处理](#1. 边界条件处理)
- [2. 调试方法](#2. 调试方法)
- 七、代码模版(c++)
- 八、经典例题
- 九、总结与学习建议
-
- [1. 核心总结](#1. 核心总结)
- [2. 学习建议](#2. 学习建议)

一、二叉堆的核心思想与本质
核心思想:二叉堆是一种完全二叉树,满足堆性质(父节点的值总是大于或小于子节点的值)。
最大堆:父节点的值大于或等于子节点的值。
最小堆:父节点的值小于或等于子节点的值。
本质:通过维护堆性质,实现高效的插入、删除和最值查询操作。
二、二叉堆的实现与操作
1. 存储结构
数组存储:利用完全二叉树的性质,将堆存储在一维数组中。
对于下标为 i 的节点:
父节点:(i - 1) / 2
左子节点:2 * i + 1
右子节点:2 * i + 2
2. 基本操作
插入(Insert) :
将新元素插入数组末尾。
从新元素开始向上调整(上浮),直到满足堆性质。
删除堆顶(Extract-Max/Min):
将堆顶元素与数组末尾元素交换。
删除末尾元素。
从堆顶开始向下调整(下沉),直到满足堆性质。
建堆(Heapify) :
从最后一个非叶子节点开始,依次向下调整。
三、二叉堆的应用场景
1. 优先队列
实现优先级调度:每次取出优先级最高的元素。
示例:Dijkstra 算法中用于选择最短路径。
2. Top-K 问题
问题描述:从大量数据中找出前 K 个最大或最小的元素。
解法:使用最小堆维护前 K 个最大元素,或使用最大堆维护前 K 个最小元素。
3. 堆排序
步骤 :
建堆。
依次将堆顶元素与末尾元素交换,并调整堆。
时间复杂度 :
O(nlogn)。
4. 中位数维护
问题描述:动态维护数据流的中位数。
解法:使用一个最大堆和一个最小堆,分别存储较小的一半和较大的一半。
四、二叉堆的复杂度分析
时间复杂度
插入:
O(logn)
删除堆顶:
O(logn)
建堆:
O(n)(通过数学证明,建堆的摊还复杂度为 O(n))
堆排序:
O(nlogn)
空间复杂度
存储堆:
O(n)
五、二叉堆的变种与高阶应用
1. 二项堆
特点:由多个二项树组成,支持高效的合并操作。
应用:图算法中的优先级队列。
2. 斐波那契堆
特点:支持更高效的插入、合并和减小键值操作。
应用:Dijkstra 算法和 Prim 算法。
3. 左偏堆
特点:支持高效的合并操作,适合需要频繁合并堆的场景。
六、常见陷阱与调试技巧
1. 边界条件处理
空堆检查:在删除堆顶前需判断堆是否为空。
数组越界:在调整堆时注意下标范围。
2. 调试方法
打印堆状态:在每次操作后输出堆的内容。
可视化堆结构:手动画出堆的树形结构,检查堆性质。
七、代码模版(c++)
cpp
#include<iostream>
using namespace std;
#define maxn 100001
#define lson(idx)(idx*2+1)
#define rson(idx)(idx*2+2)
#define parent(idx)((idx-1)/2)
template<typename T>
struct Small{
bool operator()(const T& left, const T& right) const {
return left < right;
}
};
template<typename T>
struct Big {
bool operator()(const T& left, const T& right) const {
return left > right;
}
};
template<class T,typename cmp>
class Heap {
private:
void shiftUp(int curr);
void shiftDown(int curr);
public:
Heap(int cap = maxn);
~Heap();
void push(const T& data);
void pop();
T top() const;
bool empty() const;
void clear();
private:
T* m_data;
int m_size;
int m_capacity;
cmp m_cmp;
};
template<class T, typename cmp>
void Heap<T, cmp>::shiftUp(int curr){
if (curr == 0)return;
int par = parent(curr);
if (m_cmp(m_data[curr], m_data[par])) {
swap(m_data[curr], m_data[par]);
shiftUp(par);
}
}
template<class T, typename cmp>
void Heap<T, cmp>::shiftDown(int curr){
int lsonId = lson(curr);
int rsonId = rson(curr);
int optId = curr;
if (lsonId < m_size && m_cmp(m_data[lsonId], m_data[optId])) {
optId = lsonId;
}
if (rsonId < m_size && m_cmp(m_data[rsonId], m_data[optId])) {
optId = rsonId;
}
if (curr != optId) {
swap(m_data[optId], m_data[curr]);
shiftDown(optId);
}
}
template<class T, typename cmp>
Heap<T, cmp>::Heap(int cap){
m_data = new T[cap];
m_capacity = cap;
m_size = 0;
}
template<class T, typename cmp>
Heap<T, cmp>::~Heap(){
delete[] m_data;
m_data = NULL;
}
template<class T, typename cmp>
void Heap<T, cmp>::push(const T& data){
m_data[m_size++] = data;
shiftUp(m_size - 1);
}
template<class T, typename cmp>
void Heap<T, cmp>::pop(){
swap(m_data[0], m_data[m_size - 1]);
m_size--;
shiftDown(0);
}
template<class T, typename cmp>
T Heap<T, cmp>::top() const{
return m_data[0];
}
template<class T, typename cmp>
bool Heap<T, cmp>::empty() const{
return m_size == 0;
}
template<class T, typename cmp>
void Heap<T, cmp>::clear(){
m_size = 0;
}
int main() {
Heap<int, Big<int>> h;
for (int i = 0; i < 5; i++) {
int x = rand() % 30;
int y = rand() % 30;
h.push(x), h.push(y);
cout << "塞入两个数:" << x << ' ' << y << endl;
cout << "目前最大的数是" << h.top() << endl;
h.pop();
cout << "弹出最大的那个数" << endl;
}
return 0;
}
八、经典例题
小蓝的最大集合
cpp
#include<iostream>
using namespace std;
#define maxn 200001
#define lson(idx)(idx*2+1)
#define rson(idx)(idx*2+2)
#define parent(idx)((idx-1)/2)
template<typename T>
struct Small {
bool operator()(const T& left, const T& right) const {
return left < right;
}
};
template<typename T>
struct Big {
bool operator()(const T& left, const T& right) const {
return left > right;
}
};
template<class T,typename cmp>
class Heap {
private:
void shiftUp(int curr);
void shiftDown(int curr);
T* m_data;
int m_size;
int m_capacity;
cmp m_cmp;
public:
Heap(int cap=maxn);
~Heap();
void push(const T& data);
void pop();
T top() const;
void clear();
bool empty() const;
int size();
T* getData();
};
template<class T, typename cmp>
void Heap<T, cmp>::shiftUp(int curr){
if (curr == 0)return;
int par = parent(curr);
if (m_cmp(m_data[curr], m_data[par])) {
swap(m_data[par], m_data[curr]);
shiftUp(par);
}
}
template<class T, typename cmp>
void Heap<T, cmp>::shiftDown(int curr) {
int lsonId = lson(curr);
int rsonId = rson(curr);
int optId = curr;
if (lsonId < m_size && m_cmp(m_data[lsonId], m_data[optId])) {
optId = lsonId;
}
if (rsonId < m_size && m_cmp(m_data[rsonId], m_data[optId])) {
optId = rsonId;
}
if (curr != optId) {
swap(m_data[curr], m_data[optId]);
shiftDown(optId);
}
}
template<class T, typename cmp>
Heap<T, cmp>::Heap(int cap) {
m_size = 0;
m_data = new T[cap];
m_capacity = cap;
}
template<class T, typename cmp>
Heap<T, cmp>::~Heap() {
m_size = 0;
delete[] m_data;
m_data = NULL;
}
template<class T, typename cmp>
void Heap<T, cmp>::push(const T& data) {
m_data[m_size++] = data;
shiftUp(m_size - 1);
}
template<class T, typename cmp>
void Heap<T, cmp>::pop() {
swap(m_data[0], m_data[m_size - 1]);
m_size--;
shiftDown(0);
}
template<class T, typename cmp>
T Heap<T, cmp>::top() const {
return m_data[0];
}
template<class T, typename cmp>
void Heap<T, cmp>::clear() {
m_size = 0;
}
template<class T, typename cmp>
bool Heap<T, cmp>::empty() const {
return m_size == 0;
}
template<class T, typename cmp>
int Heap<T, cmp>::size() {
return m_size;
}
template<class T, typename cmp>
T* Heap<T, cmp>::getData() {
return m_data;
}
int main() {
Heap<int, Big<int>> h;
int n, k;
cin >> n >> k;
h.push(k);
while (n--) {
int opt;
cin >> opt;
if (opt == 1) {
int x;
cin >> x;
h.push(x);
}
else if (opt == 2) {
int x;
cin >> x;
int* data = h.getData();
for (int i = 0; i < h.size(); i++) {
data[i] += x;
}
}
else if (opt == 3) {
int x;
cin >> x;
int* data = h.getData();
for (int i = 0; i < h.size(); i++) {
data[i] -= x;
}
}
else {
cout << h.top() << endl;
h.pop();
}
}
cout << endl;
return 0;
}
还有一种方法
cpp
Heap<int, Big<int>> h;
int n, k;
cin >> n >> k;
h.push(k);
int sum = 0; //用于记录数字串中被加减后的值
while (n--) {
int opt;
cin >> opt;
if (opt == 4) {
cout << h.top() + sum << endl;
h.pop();
}
else {
int x;
cin >> x;
if (opt == 1) {
h.push(x - sum);
}
else if (opt == 2) {
sum += x;
}
else if (opt == 3) {
sum -= x;
}
}
}
cout << endl;
第k大的数
cpp
#include<iostream>
using namespace std;
#define maxn 200001
#define lson(idx)(idx*2+1)
#define rson(idx)(idx*2+2)
#define parent(idx)((idx-1)/2)
template<typename T>
struct Small {
bool operator()(const T& left, const T& right) const {
return left < right;
}
};
template<typename T>
struct Big {
bool operator()(const T& left, const T& right) const {
return left > right;
}
};
template<class T,typename cmp>
class Heap {
private:
void shiftUp(int curr);
void shiftDown(int curr);
T* m_data;
int m_size;
int m_capacity;
cmp m_cmp;
public:
Heap(int cap=maxn);
~Heap();
void push(const T& data);
void pop();
T top() const;
void clear();
bool empty() const;
int size();
T* getData();
};
template<class T, typename cmp>
void Heap<T, cmp>::shiftUp(int curr){
if (curr == 0)return;
int par = parent(curr);
if (m_cmp(m_data[curr], m_data[par])) {
swap(m_data[par], m_data[curr]);
shiftUp(par);
}
}
template<class T, typename cmp>
void Heap<T, cmp>::shiftDown(int curr) {
int lsonId = lson(curr);
int rsonId = rson(curr);
int optId = curr;
if (lsonId < m_size && m_cmp(m_data[lsonId], m_data[optId])) {
optId = lsonId;
}
if (rsonId < m_size && m_cmp(m_data[rsonId], m_data[optId])) {
optId = rsonId;
}
if (curr != optId) {
swap(m_data[curr], m_data[optId]);
shiftDown(optId);
}
}
template<class T, typename cmp>
Heap<T, cmp>::Heap(int cap) {
m_size = 0;
m_data = new T[cap];
m_capacity = cap;
}
template<class T, typename cmp>
Heap<T, cmp>::~Heap() {
m_size = 0;
delete[] m_data;
m_data = NULL;
}
template<class T, typename cmp>
void Heap<T, cmp>::push(const T& data) {
m_data[m_size++] = data;
shiftUp(m_size - 1);
}
template<class T, typename cmp>
void Heap<T, cmp>::pop() {
swap(m_data[0], m_data[m_size - 1]);
m_size--;
shiftDown(0);
}
template<class T, typename cmp>
T Heap<T, cmp>::top() const {
return m_data[0];
}
template<class T, typename cmp>
void Heap<T, cmp>::clear() {
m_size = 0;
}
template<class T, typename cmp>
bool Heap<T, cmp>::empty() const {
return m_size == 0;
}
template<class T, typename cmp>
int Heap<T, cmp>::size() {
return m_size;
}
template<class T, typename cmp>
T* Heap<T, cmp>::getData() {
return m_data;
}
int main() {
Heap<int, Small<int>> h;
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++) {
int a;
cin >> a;
h.push(a);
}
while (k--) {
int x;
cin >> x;
if (x >= h.top()) {
h.pop();
h.push(x);
}
cout << h.top() << ' ';
}
cout << endl;
return 0;
}
一道简单的取模问题
cpp
#include<iostream>
using namespace std;
#define maxn 200001
#define lson(idx)(idx*2+1)
#define rson(idx)(idx*2+2)
#define parent(idx)((idx-1)/2)
template<typename T>
struct Small {
bool operator()(const T& left, const T& right) const {
return left < right;
}
};
template<typename T>
struct Big {
bool operator()(const T& left, const T& right) const {
return left > right;
}
};
template<class T,typename cmp>
class Heap {
private:
void shiftUp(int curr);
void shiftDown(int curr);
T* m_data;
int m_size;
int m_capacity;
cmp m_cmp;
public:
Heap(int cap=maxn);
~Heap();
void push(const T& data);
void pop();
T top() const;
void clear();
bool empty() const;
int size();
T* getData();
};
template<class T, typename cmp>
void Heap<T, cmp>::shiftUp(int curr){
if (curr == 0)return;
int par = parent(curr);
if (m_cmp(m_data[curr], m_data[par])) {
swap(m_data[par], m_data[curr]);
shiftUp(par);
}
}
template<class T, typename cmp>
void Heap<T, cmp>::shiftDown(int curr) {
int lsonId = lson(curr);
int rsonId = rson(curr);
int optId = curr;
if (lsonId < m_size && m_cmp(m_data[lsonId], m_data[optId])) {
optId = lsonId;
}
if (rsonId < m_size && m_cmp(m_data[rsonId], m_data[optId])) {
optId = rsonId;
}
if (curr != optId) {
swap(m_data[curr], m_data[optId]);
shiftDown(optId);
}
}
template<class T, typename cmp>
Heap<T, cmp>::Heap(int cap) {
m_size = 0;
m_data = new T[cap];
m_capacity = cap;
}
template<class T, typename cmp>
Heap<T, cmp>::~Heap() {
m_size = 0;
delete[] m_data;
m_data = NULL;
}
template<class T, typename cmp>
void Heap<T, cmp>::push(const T& data) {
m_data[m_size++] = data;
shiftUp(m_size - 1);
}
template<class T, typename cmp>
void Heap<T, cmp>::pop() {
swap(m_data[0], m_data[m_size - 1]);
m_size--;
shiftDown(0);
}
template<class T, typename cmp>
T Heap<T, cmp>::top() const {
return m_data[0];
}
template<class T, typename cmp>
void Heap<T, cmp>::clear() {
m_size = 0;
}
template<class T, typename cmp>
bool Heap<T, cmp>::empty() const {
return m_size == 0;
}
template<class T, typename cmp>
int Heap<T, cmp>::size() {
return m_size;
}
template<class T, typename cmp>
T* Heap<T, cmp>::getData() {
return m_data;
}
int main() {
Heap<int, Big<int>> h;
int n, q;
cin >> n >> q;
long long sum = 0;
for (int i = 0; i < n; i++) {
int a;
cin >> a;
sum += a;
h.push(a);
}
while (q--) {
int x;
cin >> x;
while (!h.empty()) {
if (h.top() >= x) {
sum -= h.top();
int y = h.top() % x;
sum += y;
h.pop();
h.push(y);
}
else break;
}
cout << sum << ' ';
}
cout << endl;
return 0;
}
九、总结与学习建议
1. 核心总结
二叉堆的核心是利用完全二叉树和堆性质实现高效的最值操作。
高阶问题的突破口通常在于堆的变种(如二项堆、斐波那契堆)或堆的应用场景(如优先队列、Top-K 问题)。
2. 学习建议
分类刷题:按问题类型集中练习(如优先队列、堆排序、Top-K 问题)。
对比其他数据结构:理解二叉堆与平衡二叉树的异同。
手写模拟过程:在纸上画出堆的调整过程,加深直观理解。

希望大家可以一键三连,后续我们一起学习,谢谢大家!!!
