优先级队列(堆)的综合练习
什么是优先级队列(堆)
优先级队列通常使用堆来构建,在逻辑上是一颗二叉树,但在物理上堆是储存在数组中的,堆的基本作用是快速找到集合中的最值。堆分为大堆和小堆,所有节点的值都大于其子树节点的值的堆叫做大堆(大根堆),所有节点的值都小于其子树节点的值叫做小堆(小根堆)。
堆的基本操作
向下调整
从上到下依次调整每棵树,将每棵树都调整最根节点的值大于左右子树节点,从而将整个堆调整为大堆;或调整为根节点的值小于左右子树节点的值,将整个堆调整为小堆,这个过程叫做向下调整。
public void shiftDown(int[] arr,int size,int index){//调整为最小堆
int left = 2 * index + 1;
while (left < size){
int min = left;
int right = 2 * index + 2;
if (right < size){
if (arr[right] < arr[left])min = right;
}
if (arr[index] < arr[min])break;
int t = arr[index];
arr[index] = arr[min];
arr[min] = t;
index = min;
left = 2 * index +1;
}
}
入队列
首先按照尾插的方式把数据放入队尾,然后进行向上调整
public void offer(int[]arr,int index){//入队列,向上调整
while (index > 0){
int parent = (index - 1) / 2;
if (arr[parent] >= arr[index])break;
int tmp = arr[parent];
arr[parent] = arr[index];
arr[index] = tmp;
index = parent;
}
}
出队列
出队列时是吧堆顶的元素返回出去,但是并不是直接删除,是用数组中最后一个元素替换堆顶元素,然后在进行一次向下调整即可。
public int poll(int[] arr){
int tmp = arr[0];
arr[0] = arr[--size];
shiftDown(arr,size,0);
return tmp;
}
堆相关题目
查找和最小的K对数字
/*
给定两个以升序排列的整形数组 nums1 和 nums2, 以及一个整数 k。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。
找到和最小的 k 对数字 (u1,v1), (u2,v2) ... (uk,vk)。
链接:https://leetcode-cn.com/problems/find-k-pairs-with-smallest-sums
*/
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
PriorityQueue<List<Integer>> priorityQueue = new PriorityQueue<>(k, new Comparator<List<Integer>>() {
@Override
public int compare(List<Integer> o1, List<Integer> o2) {
return (o2.get(0) + o2.get(1)) - (o1.get(0) + o1.get(1));
}
});
for (int i = 0; i < Math.min(nums1.length, k); i++) {
for (int j = 0; j < Math.min(nums2.length, k); j++) {
if (priorityQueue.size() == k && nums1[i] + nums2[j] > priorityQueue.peek().get(0) + priorityQueue.peek().get(1))
break;
if (priorityQueue.size() == k) priorityQueue.poll();
List<Integer> list = new ArrayList<>();
list.add(nums1[i]);
list.add(nums2[j]);
priorityQueue.add(list);
}
}
List<List<Integer>> ret = new ArrayList<>();
while (!priorityQueue.isEmpty()) ret.add(priorityQueue.poll());
return ret;
}
最后一块石头的重量
/*
有一堆石头,每块石头的重量都是正整数。
每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 x 和 y,且 x <= y。那么粉碎的可能结果如下:
如果 x == y,那么两块石头都会被完全粉碎;
如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x。
最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。
*/
public int lastStoneWeight(int[] stones) {
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
});
for (int n : stones) priorityQueue.offer(n);
int x = 0;
int y = 0;
while (!priorityQueue.isEmpty()) {
x = priorityQueue.poll();
if (!priorityQueue.isEmpty()) y = priorityQueue.poll();
else {
y = -1;
break;
}
if (x != y) priorityQueue.offer(Math.abs(x - y));
}
if (y == -1) return x;
return 0;
}