总结
方法一:暴力法
要找到数组中的第K个最大元素,首先可以想到排序,再遍历。
此种方法的时间复杂度最低为O(nlogn)。
方法二:基于partition的分治法
首先可以想到快速排序可以找到第index大的元素。
时间复杂度为O(n)。
class Solution {
public int findKthLargest(int[] nums, int k) {
int len = nums.length;
if (k > len) {
return 0;
}
int start = 0;
int end = len - 1;
int index = partition(nums, start, end);
while (index != len-k) {
if (index > len-k) {
end = index-1;
index = partition(nums, start, end);
} else if (index < len-k) {
start = index+1;
index = partition(nums, start, end);
}
}
return nums[index];
}
// partition函数是为了找出nums[0]在排序中的第几个位置
public int partition(int[] nums, int start, int end) {
int i = start;
int j = end;
int pivot = nums[start];
if (i >= j) {
return i;
}
while (i < j) {
while (pivot <= nums[j] && i < j) {
j--;
}
while (pivot >= nums[i] && i < j) {
i++;
}
//如果满足条件则交换
if (i < j) {
int temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
}
nums[start] = nums[i];
nums[i] = pivot;
return i;
}
}
方法三:基于最小堆/优先队列的方法
可以建立一个大小为k的最小堆。堆里面的数字都是大数,如果有比最小值大的,则删除最小值,插入该数。如果比堆顶元素小,则i++。遍历完数组,堆顶元素为第k大的。在java中,我们通常用优先队列来表示堆。
在最小堆中,根节点的值总是大于它的子树中任意节点的值。于是可以在O(1)时间内得到已有的k个数字中的最大值。最大堆中创建的时间复杂度是O(nlogk),最大堆中插入的时间复杂度是O(logk),最大堆中删除的时间复杂度为O(logk)。可以在O(1)时间内得到已有的k个数字中的最大值,但需要O(logk)时间完成删除及插入操作。
class Solution {
public int findKthLargest(int[] nums, int k) {
// 新建一个比较器,用该比较器实现一个优先队列
Comparator<Integer> queueComparator = new numComparator();
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(k, queueComparator);
for (int i = 0; i < nums.length; i++) {
if (priorityQueue.size() < k ) {
priorityQueue.add(nums[i]);
} else if (nums[i] > priorityQueue.peek()) {
// 如果有比最小值大的,则删除最小值,插入该数
priorityQueue.remove();
priorityQueue.add(nums[i]);
}
}
return priorityQueue.peek();
}
// 此Comparator类用于确定上述PriorityQueue的排序顺序。
class numComparator implements Comparator<Integer> {
@Override
public int compare(Integer x, Integer y) {
if (x < y) {
return -1;
} else if (x > y) {
return 1;
}
return 0;
}
}
}