06-堆(Heap)
💡 核心结论
堆本质
定义:完全二叉树,满足堆性质(父节点≥/≤所有子节点)
类型:最大堆(父≥子)、最小堆(父≤子)
核心:快速找到最大/最小值O(1),插入删除O(log n)
实现:用数组存储,利用完全二叉树性质计算索引
应用:优先队列、Top K问题、堆排序
数组索引公式(从0开始)
父节点: (i - 1) / 2
左子节点: 2i + 1
右子节点: 2i + 2
关键操作
操作 |
复杂度 |
说明 |
|---|---|---|
找最大/最小 |
O(1) |
堆顶元素 |
插入 |
O(log n) |
上浮调整 |
删除堆顶 |
O(log n) |
下沉调整 |
建堆 |
O(n) |
从底向上调整 |
堆 vs 其他结构
数据结构 |
找最大 |
插入 |
删除最大 |
|---|---|---|---|
无序数组 |
O(n) |
O(1) |
O(n) |
有序数组 |
O(1) |
O(n) |
O(1) |
BST |
O(log n) |
O(log n) |
O(log n) |
堆 |
O(1) |
O(log n) |
O(log n) |
应用场景(重要)
优先队列:任务调度、事件驱动
Top K问题:最大/最小的K个元素
堆排序:O(n log n)原地排序
中位数维护:双堆结构
定时器:最小堆管理定时任务
🔺 最大堆操作
上浮(Swim/Heapify Up)
插入新元素后,从底向上调整
如果子节点 > 父节点,交换
重复直到满足堆性质
下沉(Sink/Heapify Down)
删除堆顶后,将末尾元素放到堆顶
从上向下调整
与较大的子节点交换
重复直到满足堆性质
🏗️ 建堆
方法1:逐个插入
# 时间复杂度:O(n log n)
for element in array:
heap.insert(element)
方法2:自底向上(推荐)
# 时间复杂度:O(n)
# 从最后一个非叶子节点开始下沉
for i in range((len(array) - 2) // 2, -1, -1):
heapify_down(array, i)
为什么O(n):
叶子节点占一半,不需要调整
倒数第二层最多下沉1次
倒数第三层最多下沉2次
总和:n/2×0 + n/4×1 + n/8×2 + … = O(n)
🎯 堆排序
算法步骤
建立最大堆:O(n)
交换堆顶和末尾:O(1)
减小堆大小,下沉新堆顶:O(log n)
重复步骤2-3:n次
总时间复杂度:O(n log n) 空间复杂度:O(1)(原地排序)
def heap_sort(arr):
# 建堆
for i in range((len(arr) - 2) // 2, -1, -1):
heapify_down(arr, i, len(arr))
# 排序
for i in range(len(arr) - 1, 0, -1):
arr[0], arr[i] = arr[i], arr[0]
heapify_down(arr, 0, i)
💡 Top K问题
最大的K个元素
# 使用最小堆,维护K个元素
import heapq
def top_k_largest(nums, k):
heap = nums[:k]
heapq.heapify(heap) # 最小堆
for num in nums[k:]:
if num > heap[0]:
heapq.heapreplace(heap, num)
return heap
最小的K个元素
# 使用最大堆
def top_k_smallest(nums, k):
# Python heapq是最小堆,取负数实现最大堆
heap = [-x for x in nums[:k]]
heapq.heapify(heap)
for num in nums[k:]:
if num < -heap[0]:
heapq.heapreplace(heap, -num)
return [-x for x in heap]
📚 LeetCode练习
💻 完整代码实现
Python 实现
1"""
2最大堆和最小堆实现
3"""
4
5class MaxHeap:
6 """最大堆实现"""
7
8 def __init__(self):
9 self.heap = []
10
11 def _parent(self, i):
12 """父节点索引"""
13 return (i - 1) // 2
14
15 def _left_child(self, i):
16 """左子节点索引"""
17 return 2 * i + 1
18
19 def _right_child(self, i):
20 """右子节点索引"""
21 return 2 * i + 2
22
23 def _swap(self, i, j):
24 """交换两个元素"""
25 self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
26
27 def _swim(self, i):
28 """上浮"""
29 while i > 0 and self.heap[i] > self.heap[self._parent(i)]:
30 self._swap(i, self._parent(i))
31 i = self._parent(i)
32
33 def _sink(self, i):
34 """下沉"""
35 max_index = i
36 left = self._left_child(i)
37 right = self._right_child(i)
38
39 # 找出最大值的索引
40 if left < len(self.heap) and self.heap[left] > self.heap[max_index]:
41 max_index = left
42 if right < len(self.heap) and self.heap[right] > self.heap[max_index]:
43 max_index = right
44
45 # 如果最大值不是当前节点,交换并继续下沉
46 if max_index != i:
47 self._swap(i, max_index)
48 self._sink(max_index)
49
50 def insert(self, val):
51 """插入元素"""
52 self.heap.append(val)
53 self._swim(len(self.heap) - 1)
54
55 def extract_max(self):
56 """删除并返回最大值"""
57 if not self.heap:
58 raise IndexError('extract from empty heap')
59
60 if len(self.heap) == 1:
61 return self.heap.pop()
62
63 # 交换堆顶和末尾
64 max_val = self.heap[0]
65 self.heap[0] = self.heap.pop()
66 self._sink(0)
67 return max_val
68
69 def peek(self):
70 """查看最大值"""
71 if not self.heap:
72 raise IndexError('peek from empty heap')
73 return self.heap[0]
74
75 def size(self):
76 """堆大小"""
77 return len(self.heap)
78
79 def is_empty(self):
80 """是否为空"""
81 return len(self.heap) == 0
82
83 @staticmethod
84 def heapify(arr):
85 """建堆(O(n))"""
86 heap = MaxHeap()
87 heap.heap = arr[:]
88
89 # 从最后一个非叶子节点开始下沉
90 for i in range((len(arr) - 2) // 2, -1, -1):
91 heap._sink(i)
92
93 return heap
94
95 def __str__(self):
96 return str(self.heap)
97
98
99class MinHeap:
100 """最小堆(只修改比较符号)"""
101
102 def __init__(self):
103 self.heap = []
104
105 def _parent(self, i):
106 return (i - 1) // 2
107
108 def _left_child(self, i):
109 return 2 * i + 1
110
111 def _right_child(self, i):
112 return 2 * i + 2
113
114 def _swap(self, i, j):
115 self.heap[i], self.heap[j] = self.heap[j], self.heap[i]
116
117 def _swim(self, i):
118 while i > 0 and self.heap[i] < self.heap[self._parent(i)]:
119 self._swap(i, self._parent(i))
120 i = self._parent(i)
121
122 def _sink(self, i):
123 min_index = i
124 left = self._left_child(i)
125 right = self._right_child(i)
126
127 if left < len(self.heap) and self.heap[left] < self.heap[min_index]:
128 min_index = left
129 if right < len(self.heap) and self.heap[right] < self.heap[min_index]:
130 min_index = right
131
132 if min_index != i:
133 self._swap(i, min_index)
134 self._sink(min_index)
135
136 def insert(self, val):
137 self.heap.append(val)
138 self._swim(len(self.heap) - 1)
139
140 def extract_min(self):
141 if not self.heap:
142 raise IndexError('extract from empty heap')
143
144 if len(self.heap) == 1:
145 return self.heap.pop()
146
147 min_val = self.heap[0]
148 self.heap[0] = self.heap.pop()
149 self._sink(0)
150 return min_val
151
152 def peek(self):
153 if not self.heap:
154 raise IndexError('peek from empty heap')
155 return self.heap[0]
156
157
158def heap_sort(arr):
159 """堆排序"""
160 # 建立最大堆
161 heap = MaxHeap.heapify(arr)
162
163 # 依次取出最大值
164 result = []
165 while not heap.is_empty():
166 result.append(heap.extract_max())
167
168 return result[::-1] # 反转得到升序
169
170
171def top_k_largest(nums, k):
172 """最大的K个元素(使用最小堆)"""
173 if k >= len(nums):
174 return nums
175
176 heap = MinHeap()
177
178 # 先放入K个元素
179 for i in range(k):
180 heap.insert(nums[i])
181
182 # 遍历剩余元素
183 for i in range(k, len(nums)):
184 if nums[i] > heap.peek():
185 heap.extract_min()
186 heap.insert(nums[i])
187
188 return heap.heap
189
190
191def demo():
192 """演示堆操作"""
193 print("=== 最大堆演示 ===\n")
194
195 heap = MaxHeap()
196
197 # 插入
198 values = [3, 1, 6, 5, 2, 4]
199 print(f"插入: {values}")
200 for val in values:
201 heap.insert(val)
202 print(f"插入 {val}: {heap}")
203
204 print(f"\n堆顶(最大值): {heap.peek()}")
205
206 # 删除
207 print("\n依次删除最大值:")
208 while not heap.is_empty():
209 print(f"删除: {heap.extract_max()}, 堆: {heap}")
210
211 print("\n=== 建堆演示 ===\n")
212 arr = [3, 1, 6, 5, 2, 4]
213 print(f"原数组: {arr}")
214 heap2 = MaxHeap.heapify(arr)
215 print(f"建堆后: {heap2}")
216
217 print("\n=== 堆排序演示 ===\n")
218 arr = [3, 1, 6, 5, 2, 4]
219 print(f"原数组: {arr}")
220 sorted_arr = heap_sort(arr)
221 print(f"排序后: {sorted_arr}")
222
223 print("\n=== Top K问题 ===\n")
224 nums = [3, 2, 1, 5, 6, 4]
225 k = 3
226 print(f"数组: {nums}")
227 print(f"最大的{k}个元素: {top_k_largest(nums, k)}")
228
229
230if __name__ == '__main__':
231 demo()
232
C++ 实现
1/**
2 * 最大堆实现
3 */
4
5#include <iostream>
6#include <vector>
7#include <stdexcept>
8
9class MaxHeap {
10private:
11 std::vector<int> heap;
12
13 int parent(int i) {
14 return (i - 1) / 2;
15 }
16
17 int leftChild(int i) {
18 return 2 * i + 1;
19 }
20
21 int rightChild(int i) {
22 return 2 * i + 2;
23 }
24
25 void swim(int i) {
26 while (i > 0 && heap[i] > heap[parent(i)]) {
27 std::swap(heap[i], heap[parent(i)]);
28 i = parent(i);
29 }
30 }
31
32 void sink(int i) {
33 int maxIndex = i;
34 int left = leftChild(i);
35 int right = rightChild(i);
36
37 if (left < heap.size() && heap[left] > heap[maxIndex]) {
38 maxIndex = left;
39 }
40 if (right < heap.size() && heap[right] > heap[maxIndex]) {
41 maxIndex = right;
42 }
43
44 if (maxIndex != i) {
45 std::swap(heap[i], heap[maxIndex]);
46 sink(maxIndex);
47 }
48 }
49
50public:
51 void insert(int val) {
52 heap.push_back(val);
53 swim(heap.size() - 1);
54 }
55
56 int extractMax() {
57 if (heap.empty()) {
58 throw std::runtime_error("Extract from empty heap");
59 }
60
61 if (heap.size() == 1) {
62 int val = heap.back();
63 heap.pop_back();
64 return val;
65 }
66
67 int maxVal = heap[0];
68 heap[0] = heap.back();
69 heap.pop_back();
70 sink(0);
71 return maxVal;
72 }
73
74 int peek() const {
75 if (heap.empty()) {
76 throw std::runtime_error("Peek from empty heap");
77 }
78 return heap[0];
79 }
80
81 int size() const {
82 return heap.size();
83 }
84
85 bool isEmpty() const {
86 return heap.empty();
87 }
88
89 static MaxHeap heapify(const std::vector<int>& arr) {
90 MaxHeap h;
91 h.heap = arr;
92
93 for (int i = (arr.size() - 2) / 2; i >= 0; i--) {
94 h.sink(i);
95 }
96
97 return h;
98 }
99
100 void print() const {
101 std::cout << "[";
102 for (size_t i = 0; i < heap.size(); i++) {
103 std::cout << heap[i];
104 if (i < heap.size() - 1) std::cout << ", ";
105 }
106 std::cout << "]" << std::endl;
107 }
108};
109
110
111// 堆排序
112std::vector<int> heapSort(std::vector<int> arr) {
113 MaxHeap heap = MaxHeap::heapify(arr);
114 std::vector<int> result;
115
116 while (!heap.isEmpty()) {
117 result.push_back(heap.extractMax());
118 }
119
120 std::reverse(result.begin(), result.end());
121 return result;
122}
123
124
125int main() {
126 std::cout << "=== 最大堆演示 ===" << std::endl << std::endl;
127
128 MaxHeap heap;
129
130 std::vector<int> values = {3, 1, 6, 5, 2, 4};
131 std::cout << "插入: ";
132 for (int val : values) {
133 std::cout << val << " ";
134 }
135 std::cout << std::endl;
136
137 for (int val : values) {
138 heap.insert(val);
139 std::cout << "插入 " << val << ": ";
140 heap.print();
141 }
142
143 std::cout << "\n堆顶(最大值): " << heap.peek() << std::endl;
144
145 std::cout << "\n依次删除最大值:" << std::endl;
146 while (!heap.isEmpty()) {
147 std::cout << "删除: " << heap.extractMax() << ", 堆: ";
148 heap.print();
149 }
150
151 std::cout << "\n=== 建堆演示 ===" << std::endl << std::endl;
152 std::vector<int> arr = {3, 1, 6, 5, 2, 4};
153 std::cout << "原数组: ";
154 for (int val : arr) std::cout << val << " ";
155 std::cout << std::endl;
156
157 MaxHeap heap2 = MaxHeap::heapify(arr);
158 std::cout << "建堆后: ";
159 heap2.print();
160
161 std::cout << "\n=== 堆排序演示 ===" << std::endl << std::endl;
162 std::vector<int> sorted = heapSort(arr);
163 std::cout << "排序后: ";
164 for (int val : sorted) std::cout << val << " ";
165 std::cout << std::endl;
166
167 return 0;
168}