PriorityQueue
Java自带的最优队列,其内部实现方法是一个小顶堆二叉树。最小的节点永远在上方,小顶堆在做前K个这类问题是有比全部排序好的效果。O(Klogn) k<n而整体快排也有O(nlogn)的时间复杂度,所以PriorityQueue是我们必须掌握的一种数据结构。
LeetCode 5556. 可以到达的最远建筑
给你一个整数数组 heights ,表示建筑物的高度。另有一些砖块 bricks 和梯子 ladders 。
你从建筑物 0 开始旅程,不断向后面的建筑物移动,期间可能会用到砖块或梯子。
当从建筑物 i 移动到建筑物 i+1(下标 从 0 开始 )时:
如果当前建筑物的高度 大于或等于 下一建筑物的高度,则不需要梯子或砖块
如果当前建筑的高度 小于 下一个建筑的高度,您可以使用 一架梯子 或 (h[i+1] - h[i]) 个砖块
如果以最佳方式使用给定的梯子和砖块,返回你可以到达的最远建筑物的下标(下标 从 0 开始 )。
示例 1:
输入:heights = [4,2,7,6,9,14,12], bricks = 5, ladders = 1
输出:4
解释:从建筑物 0 出发,你可以按此方案完成旅程:
- 不使用砖块或梯子到达建筑物 1 ,因为 4 >= 2
- 使用 5 个砖块到达建筑物 2 。你必须使用砖块或梯子,因为 2 < 7
- 不使用砖块或梯子到达建筑物 3 ,因为 7 >= 6
- 使用唯一的梯子到达建筑物 4 。你必须使用砖块或梯子,因为 6 < 9
无法越过建筑物 4 ,因为没有更多砖块或梯子。
示例 2:
输入:heights = [4,12,2,7,3,18,20,3,19], bricks = 10, ladders = 2
输出:7
示例 3:
输入:heights = [14,3,19,3], bricks = 17, ladders = 0
输出:3
提示:
1 <= heights.length <= 105
1 <= heights[i] <= 106
0 <= bricks <= 109
0 <= ladders <= heights.length
力扣5556
第一次思路(递归)
每一步取使用梯子和砖块两者最优方法
class Solution {
public int furthestBuilding(int[] heights, int bricks, int ladders) {
return DFS(heights, bricks, ladders, 0);
}
public int DFS(int[] heights, int bicks, int ladders, int i) {
//如果梯子或砖块用超了,直接返回
if (ladders<0||bicks < 0) {
return i - 1;
}if (i == heights.length - 1) {
return i;
}
int now = heights[i + 1] - heights[i];
if (now <= 0) {
//如果这个建筑高度大于下一块建筑,可以直接跳过去
return DFS(heights, bicks, ladders, i + 1);
} else {
//比较两种选择的最优解。
int a = DFS(heights, bicks, ladders - 1, i + 1);
int b = DFS(heights, bicks - now, ladders, i + 1);
return Math.max(a,b);
}
}
}
这个方法思路没问题,就是太复杂了。以下是我看题解结合自己百度学到的新方法。
优先队列+贪心
创建一个小顶堆,小顶堆我简单介绍一下。它可以每次返回一组数中最小的数,他是一个二叉树,上面的节点数值比较小。每次可以返回数顶的节点。Java里自带的小顶堆是PriorityQueue,我们只需讲高度差最大的用梯子,其他都用砖块,代码特别的巧妙实现如下:
public int furthestBuilding(int[] heights, int bricks, int ladders) {
PriorityQueue<Integer> pq = new PriorityQueue<>();
int n = heights.length;
for (int i = 0; i < n - 1; i++) {
int a = heights[i + 1] - heights[i];
if (a > 0) {
pq.offer(a);
if (pq.size() > ladders) {
bricks -= pq.poll();
}
if (bricks < 0) {
return i;
}
}
}
return n - 1;
}
LeetCode 373. 查找和最小的K对数字
这道题中,我们只需要把优先队列的排列规则自定义就行,在PriorityQuery的构造函数中传入一个自定义的Comparator,实现对每个集合中的前两个值的和比较。
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
PriorityQueue<List<Integer>> priorityQueue = new PriorityQueue<>(new Comparator<List<Integer>>() {
@Override
public int compare(List<Integer> o1, List<Integer> o2) {
int sumO1 = o1.get(0) + o1.get(1);
int sumO2 = o2.get(0) + o2.get(1);
return sumO1-sumO2;
}
});
for (int i = 0; i < nums1.length; i++) {
for (int j = 0; j < nums2.length; j++) {
List<Integer> list=new ArrayList<Integer>();
list.add(nums1[i]);
list.add(nums2[j]);
priorityQueue.add(list);
}
}
List<List<Integer>> lists=new ArrayList<>();
while(k-->0){
List<Integer> list=priorityQueue.poll();
if(list==null){
continue;
}
lists.add(list);
}
return lists;
}