堆(Heap)是计算机科学中一种常用且高效的数据结构,通常被实现为一个完全二叉树。这个树形结构中的每个节点都有一个关联的值,并且这个值遵循一定的规则,从而使得堆具备特殊的性质,主要分为两种类型:大顶堆和小顶堆。
基本概念:
-
完全二叉树:这是一种特殊的二叉树,除了最后一层外,每一层都被完全填满,最后一层的节点都尽可能地靠左排列。
-
数组表示:由于完全二叉树的特性,堆常通过数组来紧凑存储,其中数组的索引关系反映了节点间的父子关系。例如,在数组中,索引i的节点的父节点是索引i/2(向下取整),而其左、右孩子分别是索引2i和2i+1。
堆的性质:
-
大顶堆:在每个节点上,父节点的值总是大于或等于其孩子的值。因此,堆的根节点是整个堆中的最大值。
-
小顶堆:与大顶堆相反,每个节点的值总是小于或等于其孩子的值,使得堆的根节点是整个堆中的最小值。
1 代码实现
package org.example.code;
/**
* @author Mazai-Liu
* @time 2024/6/22
*/
public class MyHeap {
private int[] heapArray;
private int heapSize;
public MyHeap(int maxHeapSize) {
heapArray = new int[maxHeapSize + 1];
heapSize = 0;
heapArray[0] = Integer.MIN_VALUE; // 使用哨兵元素,简化索引计算
}
public MyHeap(int[] nums) {
heapArray = new int[nums.length + 1];
System.arraycopy(nums, 0 , heapArray, 1, nums.length);
heapSize = nums.length;
heapArray[0] = Integer.MIN_VALUE; // 使用哨兵元素,简化索引计算
for(int i = heapSize / 2; i >= 1; i --) {
minHeapify(i);
}
}
// 向堆中插入一个元素
public void insert(int value) {
if (heapSize == heapArray.length - 1) {
throw new IllegalStateException("Heap is full.");
}
heapSize++;
heapArray[heapSize] = value;
int current = heapSize;
while (heapArray[current] < heapArray[current / 2]) {
swap(current, current / 2);
current = current / 2;
}
}
// 删除并返回堆中的最小元素
public int top() {
if (isEmpty()) {
throw new IllegalStateException("Heap is empty.");
}
int root = heapArray[1];
heapArray[1] = heapArray[heapSize];
heapSize--;
minHeapify(1);
return root;
}
// 查看堆顶元素
public int peek() {
if (isEmpty()) {
throw new IllegalStateException("Heap is empty.");
}
return heapArray[1];
}
// 检查堆是否为空
public boolean isEmpty() {
return heapSize == 0;
}
// 调整堆,使其满足小根堆性质
private void minHeapify(int index) {
int left = 2 * index;
int right = 2 * index + 1;
int smallest = index;
if (left <= heapSize && heapArray[left] < heapArray[index]) {
smallest = left;
}
if (right <= heapSize && heapArray[right] < heapArray[smallest]) {
smallest = right;
}
if (smallest != index) {
swap(index, smallest);
minHeapify(smallest);
}
}
// 交换两个元素的位置
private void swap(int i, int j) {
int temp = heapArray[i];
heapArray[i] = heapArray[j];
heapArray[j] = temp;
}
// 主函数用于测试
public static void main(String[] args) {
int[] nums = new int[]{3,1,9,5,4,6,7,2,0,8};
MyHeap minHeap = new MyHeap(nums);
while(!minHeap.isEmpty()) {
System.out.println(minHeap.top());
}
}
}
2 用途
-
优先级队列:堆是实现优先级队列的常见方式,可以快速插入元素和删除(提取)最高优先级的元素(即堆顶元素)。
-
堆排序:利用堆的性质可以进行高效的排序,特别是对于大量数据的排序非常有效。
-
任务调度:在操作系统中用于决定哪些进程或任务优先执行。
-
Top k、数据流中位数问题。