优先队列在插入元素的时候,自动将其插入到合适的位置,实现动态的调整,底层是堆。创建优先队列时,可自己实现comparator接口的compare方法,用于队列中的元素排列规则。在使用优先队列时,有时候需要配合排序数组使用,也可以为Arrasy.sort()方法传入一个comparator接口的匿名内部类或lamda表达式,自定义数组排序规则。
力扣630. 课程表 III
思路:贪心+优先队列。每次选择一个截至时间最早的课程,判断该课程的持续时间与之前课程的持续时间之和是否超过了该课程的截至时间,如果没超过,则将该课程加入到队列中,并且总时间也加上该课程的持续时间。如果超过了,在判断该课程的持续时间是否小于 等于之前的课程中最大的持续时间,如果是,将该课程替换掉那个最大的课程。
所以,可以使用一个优先队列,保存的是已经选修的课程,队头是持续时间最大的课程。将所有课程按截至时间升序排序,每次选一个课程,将该课程的持续时间加上之前的持续时间之和,再和该课程的截至时间作比较。
代码:
class Solution {
public int scheduleCourse(int[][] courses) {
PriorityQueue<Integer> cour = new PriorityQueue<Integer>((a, b) -> b - a);
Arrays.sort(courses, (a, b) -> a[1] - b[1]);
int count = 0;
for(int[] course : courses){
int duration = course[0], deadline = course[1];
if( duration + count <= deadline){
count += duration;
cour.add(duration);
}else if(!cour.isEmpty() && cour.peek() > duration){
count -= cour.peek();
cour.poll();
count += duration;
cour.add(duration);
}
}
return cour.size();
}
}
复杂度分析:
时间: o(nlog(n)),调整一次需要log(n),需要n次
空间: o(n)。n元素
253. 会议室 II
思路:要安排会议室,一个很自然的想法是,会议开始的早的先安排,每当有新的会议需要 开的时候,就去已经安排的会议室查看是否空闲,如果不空,则增加新的会议。所以可以对会议按开始时间升序排序,优先队列存的是已经安排了的会议的结束时间,队头是截至时间最 小的,队列中有几个会议,就有几间会议室。当新的会议的开始时间比队头小时,就说明队头会议开完了,将其移除,加入新的会议,否则新开一间会议室,并将其移除。
代码:
class Solution {
public int minMeetingRooms(int[][] intervals) {
if(intervals == null || intervals.length == 0)return 0;
PriorityQueue<Integer> mettings = new PriorityQueue<>(
intervals.length,
new Comparator<Integer>(){
public int compare(Integer a, Integer b){
return a - b;
}
});
Arrays.sort(intervals,
new Comparator<int[]>(){
public int compare(int[] a, int[] b){
return a[0] - b[0];
}
});
mettings.add(intervals[0][1]);
for(int i = 1; i < intervals.length; i++){
if(mettings.peek() <= intervals[i][0]){
mettings.poll();
}
mettings.add(intervals[i][1]);
}
return mettings.size();
}
}
复杂度分析:
时间:o(nlog(n))
空间:o(n)
215. 数组中的第K个最大元素
思路:使用优先队列,移除k - 1次队头元素之后的队头元素就是目标元素。优先队列一般可以调用API,这里最好还是自己建立一个大根堆,调整k - 1之后的堆顶元素就是结果啦。
代码:
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> q = new PriorityQueue<>(
new Comparator<Integer>(){
public int compare(Integer a, Integer b){
return b - a;
}
});
for(int i = 0; i < nums.length; i++){
q.add(nums[i]);
}
while(k > 1){
q.poll();
k--;
}
return q.peek();
}
}
class Solution {
public int findKthLargest(int[] nums, int k) {
buildMaxHeap(nums);
for(int i = nums.length - 1; i > nums.length - k; i--){
swap(nums, 0, i);
adjust(nums, 0, i);
}
return nums[0];
}
void buildMaxHeap(int[] arr){
for(int i = arr.length / 2 - 1; i >= 0; i--){
adjust(arr, i, arr.length);
}
}
void adjust(int[] arr, int i, int heapsize){
int largest = i;
int left = 2 * i + 1;
int right = left + 1;
if(left < heapsize && arr[left] > arr[largest])largest = left;
if(right < heapsize && arr[right] > arr[largest])largest = right;
if(largest != i){
swap(arr, i, largest);
adjust(arr, largest, heapsize);
}
}
void swap(int[] arr, int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
复杂度分析:
时间:o(n(log(n)))
空间:o(n)
23. 合并K个升序链表
思路:用优先队列保存每一个链表的表头,按链表元素升序排列。每次取链表头元素,加入到新链表中,并将取出的链表元素的下一个元素入队。当对为空时,就建好了链表。
代码:
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
PriorityQueue<ListNode> queue = new PriorityQueue<>((a, b) -> a.val - b.val);
for(ListNode list : lists){
if(list != null){
queue.offer(list);
}
}
ListNode dummy = new ListNode(0);
ListNode curNode = dummy;
while(!queue.isEmpty()){
ListNode node = queue.poll();
curNode.next = node;
curNode = curNode.next;
if(node.next != null){
queue.offer(node.next);
}
}
return dummy.next;
}
}
复杂度分析:
时间:优先队列不超过k个元素,插入一个元素需要log(k),每条链表长度为n,复杂度为(nk log(k))
空间:o(k)。k条链表,最多k个链表头元素在队列中。