特性
- 堆是一颗完全二叉树,包含两种类型:大顶堆和小顶堆。
- 大顶堆满足特点:每个节点比其子树的所有节点都大。
- 小顶堆满足特点:每个节点比其子树的所有节点都小。
- 完全二叉树:
- 父亲节点为 (index + 1) / 2-1
- 左孩子 index * 2 + 1
- 右孩子 index * 2 + 2
操作
- heapify(arr[]):将一个数组调整为堆。
- siftDown(i):拿走元素,向下调整。将第i个元素拿走后,向下调整其子树。
- siftUp(i):添加元素,向上调整。第i个元素为新添加值,向上进行调整。
- 初始化堆 heapify(arr[])
- 移除堆顶元素,siftdown(0)
- 添加新元素, siftUp(heapSize-1)
场景
- 计算第K大值:用一个空间为k的小顶堆,每次与堆顶进行比较,如果比堆顶大,则替换堆顶元素,并进行调整。练习题地址
源码
/**
* 数据流中的第K大元素
* KthLargest
*/
public class Solution703 {
MinHeap minHeap;
public Solution703(int k, int[] nums) {
minHeap = new MinHeap(k);
for (int i = 0; i < nums.length; ++i){
minHeap.push(nums[i]);
}
}
public int add(int val) {
minHeap.push(val);
System.out.println(minHeap.min());
return minHeap.min();
}
/**
* 小顶堆:
*/
class MinHeap {
/**
* 最后一个元素下标+1
*/
int heapSize = 0;
int[] nums;
public MinHeap(int capacity){
nums = new int[capacity];
}
public void push(int data){
if (heapSize == nums.length){
if (data > nums[0]){
nums[0] = data;
downAdjust(0);
}
}else {
nums[heapSize] = data;
heapSize++;
upAdjust(heapSize-1);
}
}
public int min(){
return heapSize > 0 ? nums[0] : -1;
}
public int pop(){
if (heapSize == 0){
return -1;
}
int result = nums[0];
if (heapSize > 1){
nums[0] = nums[heapSize - 1];
adjust();
}
heapSize--;
return result;
}
/**
* 全调整
*/
public void adjust(){
for (int i = heapSize - 1; i >= 1; ){
int minIndex;
if ((heapSize-1) % 2 == 0){
minIndex = nums[i] < nums[i-1] ? i : i-1;
i -= 2;
}else {
minIndex = i;
i -= 1;
}
int parentIndex= (minIndex+1) / 2 - 1;
if ( nums[minIndex] < nums[parentIndex]){
int t = nums[parentIndex];
nums[parentIndex] = nums[minIndex];
nums[minIndex] = t;
}
}
}
/**
* 向上调整
* @param index
*/
public void upAdjust(int index){
int minIndex;
if (index == 0) {
return;
}
if (index % 2 == 0){
int rightChildIndex = index;
int leftChildIndex = index - 1;
minIndex = nums[leftChildIndex] < nums[rightChildIndex] ? leftChildIndex : rightChildIndex;
}else {
minIndex = index;
}
int parentIndex = ((index + 1) >> 1) - 1;
if ( nums[minIndex] < nums[parentIndex] ){
int t = nums[parentIndex];
nums[parentIndex] = nums[minIndex];
nums[minIndex] = t;
upAdjust(parentIndex);
}
}
/**
* 向下调整
* @param index
*/
public void downAdjust(int index){
int leftChildIndex = (index << 1) + 1;
int rightChildIndex = (index << 1) + 2;
int minChildIndex;
if (rightChildIndex < heapSize){
minChildIndex = nums[leftChildIndex] < nums[rightChildIndex] ? leftChildIndex : rightChildIndex;
}else if (leftChildIndex < heapSize){
minChildIndex = leftChildIndex;
}else {
return;
}
if (nums[minChildIndex] < nums[index]){
int t = nums[minChildIndex];
nums[minChildIndex] = nums[index];
nums[index] = t;
downAdjust(minChildIndex);
}
}
}
public static void main(String[] args){
int k = 3;
int[] arr = {4,5,8,2};
Solution703 kthLargest = new Solution703(3, arr);
kthLargest.add(3); // returns 4
kthLargest.add(5); // returns 5
kthLargest.add(10); // returns 5
kthLargest.add(9); // returns 8
kthLargest.add(4); // returns 8
}
}
引用
- 源码可参见java PriorityQueue