Find the kth largest element in an unsorted array. Note that it is the kth largest element in the sorted order, not the kth distinct element.
Example 1:
Input: [3,2,1,5,6,4] and k = 2
Output: 5
Example 2:
Input: [3,2,3,1,2,4,5,5,6] and k = 4
Output: 4Note:
You may assume k is always valid, 1 ≤ k ≤ array's length.
这一题最直观的做法就是做个排序,然后返回第k个元素。复杂度O(nlogn)。这个做法太傻,就不写了。。
稍微看上去好一点的是用PriorityQueue(也就是最小堆),逐个遍历数组,然后维持一个大小为k的最小堆priorityqueue,最后堆顶就是要求的答案。 给出代码如下:
public int findKthLargest(int[] nums, int k) {
PriorityQueue<Integer> pQ = new PriorityQueue<>(k);
for (int i : nums) {
if (pQ.size() < k) {
pQ.add(i);
} else if (pQ.peek() < i) {
pQ.poll();
pQ.add(i);
}
}
return pQ.peek();
}这个算法的复杂度是时间O(nlogk), 空间复杂度是o(k)。基于k <= n 这个限制,我们可以认为这个算法是优于最原始的那个的。再好一点或者说看起来高端一点的,就是一种叫selection ranki的算法。这个算法的基础是quicksort中的partition。
partition就是在数组中或者数组的给定范围中选取一个pivot(任意选取,一般是选取数组的给定范围中的最后一个或者用随机数去选取保证正态分布下是不存在最坏的情况的),然后将数组根据pivot分成左右两边,pivot左边的比pivot小,右边比pivot大。partition的代码如下:
public int partition(int[] nums, int start, int end) {
Random rand = new Random();
int pivot = rand.nextInt(end - start + 1) + start;
int k = nums[pivot];
swap(nums, pivot, end);
int j = start;
for (int i = start; i < end; i++) {
if (k < nums[i]) {
swap(nums, i, j);
j++;
}
}
swap(nums, end, j);
return j;
}在quicksort里,当你分开两边之后,你就对两边进行递归式的partition直到start和end相同为止。 public int partition(int[] nums, int start, int end) {
Random rand = new Random();
int pivot = rand.nextInt(end - start + 1) + start;
// int pivot = end;
int k = nums[pivot];
swap(nums, pivot, end);
int j = start;
for (int i = start; i < end; i++) {
if (k < nums[i]) {
swap(nums, i, j);
j++;
}
}
swap(nums, end, j);
return j;
}
public void swap(int[] arr, int a, int b) {
if (a != b) {
arr[a] ^= arr[b];
arr[b] ^= arr[a];
arr[a] ^= arr[b];
}
}
public void kPartition(int[] nums, int k) {
int start = 0, end = nums.length - 1;
while (end != k - 1) {
int pivot = partition(nums, start, end);
if (pivot == k - 1) {
return;
} else if (pivot < k - 1) {
start = pivot + 1;
} else {
end = pivot - 1;
}
}
}
public int findKthLargest(int[] nums, int k) {
kPartition(nums, k);
int min = Integer.MAX_VALUE;
for (int i = 0; i < k; i++) {
min = Math.min(nums[i], min);
}
return min;
}这种做法的复杂度分为两种说法,最坏的是O(n^2),平均是O(n)。
最坏的情况就是你选出来的pivot恰好都是当前数组范围的边界值(最大或者最小)。那么你的效率就会是n + (n - 1) + (n - 2) + ... + 1,也就是O(n^2)的级别。
但如果你的pivot服从正态分布(这也是为什么用random来选取pivot的原因,这个是一种近似正态分布的做法)。你的效率就会是 n + (n / 2) + (n / 4) + (n / 8) + ... + 1,也就是O(n)的级别。
本文介绍几种有效的方法来找出无序数组中的第K大元素,包括使用最小堆和基于快速排序的partition算法。
883

被折叠的 条评论
为什么被折叠?



