public int findKthLargest(int[] nums, int k) {
MinHeap heap = new MinHeap(k);
for (int i = 0; i < k; i++) {
heap.offer(nums[i]);
}
for (int i = k; i < nums.length; i++) {
if (nums[i] > heap.peek()) {
heap.replace(nums[i]);
}
}
return heap.peek();
}
这段 Java 代码定义了一个名为 findKthLargest 的方法,该方法接受一个整数数组 nums 和一个整数 k 作为输入,并返回数组中第 k 大的整数。该方法使用一个小顶堆数据结构来高效地找到数组中的第 k 大元素。
class MedianFinder {
private final Heap left;
private final Heap right;
public MedianFinder() {
left = new Heap(10,true);
right = new Heap(10,false);
}
/*
1. 1 3 5 <-> 7 9 10
先将数据流分为上述两部分,其中左边是一个大顶堆,右边是一个小顶堆
两部分在保持个数之差不大于1的情况下,数据流中的中位数就是
如果两部分元素个数相等 = (大顶堆中的堆顶元素+小顶堆中的堆顶元素)/2
如果两部分元素个数不等 = 大顶堆中的堆顶元素(后面讲原因)
问:如何保证两部分元素个数之差不大于1呢?
我们规定,当添加元素时,如果两部分元素个数相等,则将左部分(大顶堆)中个数加一
如果两部分元素个数不相等,则将右部分(小顶堆)中个数加一
问:如何将新元素正确加入到两个堆中呢?
我们规定,如果如果两部分元素个数相等,先将元素加入到右部分,再从右部分中拿出最小的元素加入到左部分
如果两部分元素个数不相等,先将元素加入到左部分,再从左部分中拿出最大的元素加入到右部分
*/
//添加元素
public void addNum(int num) {
if (left.getSize() == right.getSize()) {
right.offer(num);
left.offer(right.poll());
} else {
left.offer(num);
right.offer(left.poll());
}
}
//返回中位数
public double findMedian() {
if (left.getSize() == right.getSize()) {
return (left.peek() + right.peek()) / 2.0;
} else {
return left.peek();
}
}
/**
* 自动扩容 大小堆
*/
static class Heap {
private int[] array;
private int size;
private final Boolean max;
public int getSize() {
return size;
}
public Heap(int capacity, Boolean max) {
array = new int[capacity];
this.max = max;
}
/**
* 如果传入一个普通数组,要执行建堆操作,将当前数组转换为堆数组
*/
public Heap(int[] array, Boolean max) {
this.array = array;
this.size = array.length;
this.max = max;
heapify();
}
/**
* 返回堆顶元素
*/
public Integer peek() {
if (isEmpty()) {
return null;
}
return array[0];
}
/**
* 删除堆顶元素
*/
public Integer poll() {
if (isEmpty()) {
return null;
}
/*
1.先记录当前堆顶元素,方便返回结果值
2.将堆顶元素与堆中最后一个元素交换
3.删除最后一个元素
4.将推顶元素进行下潜down操作
*/
int top = array[0];
swap(0, size - 1);
size--;
down(0);
return top;
}
/**
* 删除指定索引位置的元素
*/
public Integer poll(int index) {
if (isEmpty()) {
return null;
}
int deleted = array[index];
swap(index, size - 1);
size--;
down(index);
return deleted;
}
/**
* 替换堆顶元素
*/
public boolean replace(int replaced) {
if (isEmpty()) {
return false;
}
array[0] = replaced;
down(0);
return true;
}
/**
* 向推中添加元素
*/
public boolean offer(int offered) {
if (isFull()) {
//扩容
grow();
}
/*
1.判断推是否已满
2.调用上浮up方法,将待添加元素加入到堆中合适位置
3.堆元素个数size + 1
*/
up(offered, size);
size++;
return true;
}
/**
* 扩容
*/
private void grow() {
//新建堆容量 原来的1.5倍
int newCapacity = size + (size >> 1);
int[] newArray = new int[newCapacity];
//将旧堆中的数据移入新堆中
System.arraycopy(array,0,newArray,0,size);
//更新旧的堆
array = newArray;
}
/**
* 上浮
*
* @param offered 待添加元素值
* @param index 向哪个索引位置添加
*/
private void up(int offered, int index) {
int child = index;
while (child > 0) {
int parent = (child - 1) / 2;
boolean temp = max ? offered > array[parent] : offered < array[parent];
if (temp) {
array[child] = array[parent];
} else {
break;
}
child = parent;
}
array[child] = offered;
}
/**
* 建推
*/
public void heapify() {
/*
1.找到完全二叉树的最后一个非叶子节点 公式: size / 2 - 1
2.从后向前,依次对每个飞非叶子节点调用下潜down方法
*/
for (int i = (size >> 1) - 1; i >= 0; i--) {
down(i);
}
}
/**
* 下潜
*/
private void down(int parent) {
int left = parent * 2 + 1;
int right = left + 1;
int maxOrMin = parent;
if (left < size &&
(max ? array[left] > array[maxOrMin] : array[left] < array[maxOrMin])){
maxOrMin = left;
}
if (right < size &&
(max ? array[right] > array[maxOrMin] : array[right] < array[maxOrMin])){
maxOrMin = right;
}
if (maxOrMin != parent) {
swap(maxOrMin, parent);
down(maxOrMin);
}
}
/**
* 交换
*/
private void swap(int i, int j) {
int t = array[i];
array[i] = array[j];
array[j] = t;
}
/**
* 判空
*/
public boolean isEmpty() {
return size == 0;
}
/**
* 判满
*/
public boolean isFull() {
return size == array.length;
}
}
}
2 . 使用jdk自带的数据结构PriorityQueue实现
使用JDK自带的PriorityQueue实现大顶堆与小顶堆:
大顶堆:
java复制代码
PriorityQueue<Integer> maxQue = new PriorityQueue<>((a, b) -> Integer.compare(b, a));
PriorityQueue<Integer> maxQue = new PriorityQueue<>((a, b) -> (a - b));//大顶堆推荐使用
小顶堆(默认):
java复制代码
PriorityQueue<Integer> minQue = new PriorityQueue<>((a, b) -> Integer.compare(a, b));
PriorityQueue<Integer> minQue = new PriorityQueue<>((a, b) -> (b - a));
PriorityQueue<Integer> minQue = new PriorityQueue<>(); //小顶堆默认
java复制代码
/**
* 数据流中的中位数
* 使用jdk自带的 PriorityQueue对象实现 大小顶堆
*/
@SuppressWarnings("all")
public class E03Leetcode295_jdk {
private PriorityQueue<Integer> maxQue;
private PriorityQueue<Integer> minQue;
public E03Leetcode295_jdk() {
// 大顶堆
maxQue = new PriorityQueue<>((a, b) -> Integer.compare(b, a));
// maxQue = new PriorityQueue<>((a, b) -> (a - b));
// 小顶堆(默认)
minQue = new PriorityQueue<>((a, b) -> Integer.compare(a, b));
// minQue = new PriorityQueue<>((a, b) -> (b - a));
}
//添加元素
public void addNum(int num) {
if (maxQue.size() == minQue.size()) {
minQue.offer(num);
maxQue.offer(minQue.poll());
} else {
maxQue.offer(num);
minQue.offer(maxQue.poll());
}
}
//返回中位数
public double findMedian() {
if (maxQue.size() == minQue.size()) {
return (maxQue.peek() + minQue.peek()) / 2.0;
} else {
return maxQue.peek();
}
}
}