目录
- 一、二叉堆的核心思想与本质
- 二、二叉堆的实现与操作
- 
- [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 问题)。
对比其他数据结构:理解二叉堆与平衡二叉树的异同。
手写模拟过程:在纸上画出堆的调整过程,加深直观理解。

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