# 数据流中的第 K 个最大元素
设计一个找到数据流中第 k
大元素的类(class)。注意是排序后的第 k
大元素,不是第 k
个不同的元素。
请实现 KthLargest
类:
KthLargest(int k, int[] nums)
使用整数k
和整数流nums
初始化对象。int add(int val)
将val
插入数据流nums
后,返回当前数据流中第k
大的元素。
示例:
输入:
["KthLargest", "add", "add", "add", "add", "add"]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]
解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3); // return 4
kthLargest.add(5); // return 5
kthLargest.add(10); // return 5
kthLargest.add(9); // return 8
kthLargest.add(4); // return 8
提示:
1 <= k <= 104
0 <= nums.length <= 104
-104 <= nums[i] <= 104
-104 <= val <= 104
- 最多调用
add
方法104
次 - 题目数据保证,在查找第
k
大元素时,数组中至少有k
个元素
# 题目分析:
它主要让你实现两个方法,第一个方法就是一个构造方法主要是用来初始化的,构造方法的参数 会给告诉你要求第 k 大的元素并且会给一个初始数组
public KthLargest(int k, int[] nums) { | |
} |
重要的是下面的 add 方法
public int add(int val) { | |
return -1; | |
} |
这个方法它会被不断地调用,传过来的参数就是用来模拟数据流中不断新收集到的数据,返回值就是返回第 k 大的数据
# 完整代码如下:
# 实现小堆顶的代码:
public class MinHeap | |
{ | |
public int array[]; | |
public int size; | |
public MinHeap(int capacity) | |
{ | |
this.array = new int[capacity]; | |
} | |
public boolean offer(int t) | |
{ | |
// 判断 堆 是否满了 | |
if(isFull()) | |
return false; | |
// 调用 up 方法 重新调整 小顶堆的特性 | |
up(t, size); | |
// 元素个数 自增 | |
size++; | |
return true; | |
} | |
public void up(int offered, int index) | |
{ | |
// 获取当前 index 索引位置 | |
int child = index; | |
while(child > 0) | |
{ | |
// 根据 当前 child 位置 通过公式 获取 它的父节点位置 | |
int parent = (child - 1) / 2; | |
// 判断 传入的元素值 是否 小于 parent 索引 位置的值 | |
if(offered < array[parent]) | |
// 如果小那么就将 父节点的值 向下移动 | |
array[child] = array[parent]; | |
else | |
// 如果 传入的值 大于 父节点的值 那么就不需要操作 直接 跳出循环 | |
break; | |
// 将 child 的索引 赋值为 父节点的索引值 | |
child = parent; | |
} | |
// 将 当前 child 的索引位置赋值为 传入的值 | |
array[child] = offered; | |
} | |
public void replace(int replaced) | |
{ | |
array[0] = replaced; | |
down(0); | |
} | |
public int peek() | |
{ | |
return array[0]; | |
} | |
public void swap(int i, int j) | |
{ | |
int t = array[i]; | |
array[i] = array[j]; | |
array[j] = t; | |
} | |
public void down(int parent) | |
{ | |
int left = parent * 2 + 1; | |
int right = left + 1; | |
int min = parent; | |
if(left < size && array[min] > array[left]) | |
min = left; | |
if(right < size && array[min] > array[right]) | |
min = right; | |
if(min != parent) | |
{ | |
swap(min, parent); | |
down(min); | |
} | |
} | |
public boolean isFull() | |
{ | |
return size == array.length; | |
} | |
} |
# 解决题目问题代码:
private MinHeap heap; | |
public static void main(String ... args) | |
{ | |
// 给你一个 数组 并且 要求你 求出 第 3 大的数据 | |
Tests tests = new Tests(3, new int[]{4, 5, 8, 2}); | |
// 我们使用 小顶堆实现 | |
// 我们只保留最大的 3 个元素 4 5 8 | |
// 小顶堆 4 5 8 留 最大 3 个 | |
// 第一次 3 比 小顶堆中的 4 小 所以 返回的是 4 | |
System.out.println(tests.add(3)); // [4 5 8] 4 | |
// 第二次 5 比 小顶堆中的 4 大 那么就 替换掉 4 此时小顶堆就换成了 [5 8 10] 返回 5 | |
System.out.println(tests.add(5)); // [5 5 8] 5 | |
// 第三次 10 比 小顶堆中所有的数据都大 那么就 将小顶堆 替换为 [5 8 10] 第 3 大的还是 5 | |
// PS: 小顶堆 10 比 所有数据大 它跟 堆顶元素 5 替换后 重新调整就变成了 5 8 10 了 | |
System.out.println(tests.add(10)); // [5 8 10] 5 | |
// 第四次 9 比 小顶堆中 的 8 大 那么就将小堆顶 替换为 [8 9 10] 第 3 大的是 8 | |
System.out.println(tests.add(9)); // [8 9 10] 8 | |
// 第五次 4 没有比 小顶堆中的大的 不作操作,返回第 3 大的还是 8 | |
System.out.println(tests.add(4)); // [8 9 10] 8 | |
} | |
public Tests(int k, int nums[]) | |
{ | |
heap = new MinHeap(k); | |
// 不断将 nums 中的元素 调用 add 进行添加 | |
for (int num : nums) | |
{ | |
add(num); | |
} | |
} | |
// 此方法会被不断调用,模拟数据流中新来的元素 | |
public int add(int val) | |
{ | |
// 先判断如果 堆 没有满 那么就 先将 构造函数的数组元素 添加到 堆中 后续进行 其它数据的判断与替换 | |
if(! heap.isFull()) | |
heap.offer(val); | |
// 如果传入的数据 比 堆顶 元素数据大 那么我们就需要做 替换 | |
else if(val > heap.peek()) | |
// 将当前 传入的数据 与 堆顶元素进行替换 并且 重新调整 小顶堆的特性 | |
heap.replace(val); | |
// 返回第 k 大的数据 也就是堆顶的元素 | |
return heap.peek(); | |
} |
打印结果:
4
5
5
8
8