1.数组中的第k个最大元素
思路:
- 最直观的就是选择法,遍历一k次找到第k大的数
- 之前使用快速排序的思想每次找出一个位置,会超时
- 这里使用堆(优先队列),找最大用小堆,找最小用大堆。
例如找第k大的数,新建一个空间为k的最小优先队列,只要比当前优先队列最小值大就替换进去,这样全部的数遍历一遍,里面留下的就是前k大的数了,其他的全被替换出去了,并且队头是第k最大的。
class Solution {
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> pq = new PriorityQueue<>(k);
for(int n : nums){
if(pq.size()<k){
pq.offer(n);
}else{
if(n>pq.peek()){
pq.poll();
pq.offer(n);
}
}
}
return pq.peek();
}
}
2.堆排序原理
-
构建最大堆(Max Heap):首先,将待排序的元素构建成一个最大堆。最大堆是一个满足堆性质的二叉树,即每个节点的值都大于或等于其子节点的值,根节点是堆中的最大元素。
构建最大堆的过程:从数组中的最后一个非叶子节点开始,逐个向前处理每个节点,将当前节点与其子节点比较并进行交换,直到整个数组构建成一个最大堆。 -
排序:一旦构建了最大堆,最大元素就位于堆的根节点(数组的第一个元素)。将根节点(最大元素)与数组中的最后一个元素交换,然后将数组的大小减1,将根节点下沉到适当位置,以保持剩余元素的最大堆性质。重复这个过程,直到整个数组有序。
堆排序的时间复杂度为O(n * log n),其中n是待排序数组的元素数量。由于堆排序是一种原地排序算法,不需要额外的辅助空间,因此它在空间复杂度上表现较好。堆排序的稳定性取决于在构建最大堆时是否采用稳定的下沉方法,通常情况下是不稳定的排序算法。
3.合并k个升序链表
思路:题目要求从小到大排序构造一个新的链表,这里要保证每次加入的元素是当前最小的,使用优先队列构造小根堆。将ListNode数组第一个元素加入队列,每次取出队头然后加入取出的元素的下一个。
PriorityQueue详解可以看 PriorityQueue初始化和方法
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode mergeKLists(ListNode[] lists) {
if(lists.length == 0){
return null;
}
ListNode head = new ListNode(-1);
PriorityQueue<ListNode> q = new PriorityQueue<>(lists.length,(a,b)->a.val-b.val);
for(int i = 0;i<lists.length;i++){
if(lists[i] == null) continue;
q.add(lists[i]);
}
ListNode tail = head;
while(!q.isEmpty()){
tail.next = q.poll();
tail = tail.next;
if(tail.next!=null){
q.offer(tail.next);
}
}
return head.next;
}
}