堆
堆:不只是“堆”在一起
“堆”这个名字容易让人误解为一堆杂乱无章的东西。恰恰相反,在数据结构中,堆是一种经过精心组织、能够快速找到最大或最小元素的“优先级队列”。它就像医院的急诊科,病情最重的病人总是最先得到救治。
堆的基本结构
我们通常使用二叉堆来实现,它是一种特殊的完全二叉树。它满足堆性质:每个节点的值都大于等于(大顶堆)或小于等于(小顶堆)其子节点的值。因此,堆顶元素永远是整个集合中的极值。
为什么需要堆?
当我们需要不断处理动态数据集合中的最大值或最小值时,堆是最高效的选择。用数组存储,插入和删除堆顶元素的时间复杂度仅为 O(log n),而获取极值只需 O(1)。这比每次遍历数组(O(n))或维护一个全排序数组(插入O(n))要高效得多。
堆的核心操作:上浮与下沉
堆的智慧体现在两个调整操作上:
- 上浮:当在堆尾插入新元素时,它可能比父节点大(对于大顶堆),需要不断向上交换,直到堆性质恢复。
- 下沉:当移除堆顶元素(通常用堆尾元素替换)后,新堆顶可能比孩子小,需要不断向下与更大的孩子交换,直到恢复秩序。
// 大顶堆的下沉操作核心逻辑(伪代码示意)
void siftDown(vector<int>& heap, int i) {
int maxIndex = i;
int left = 2 * i + 1;
if (left < heap.size() && heap[left] > heap[maxIndex])
maxIndex = left;
// 类似地检查右孩子...
if (maxIndex != i) {
swap(heap[i], heap[maxIndex]);
siftDown(heap, maxIndex); // 递归下沉
}
}
堆的应用场景
堆排序算法直接基于堆。更重要的是,它是实现优先队列的标准结构,应用于任务调度(CPU进程管理)、合并K个有序链表、求海量数据流中的Top K问题,以及著名的Dijkstra最短路径算法中。
总结
堆用一种“部分有序”的智慧,完美解决了“快速获取极值”这一特定问题。
浙公网安备 33010602011771号