【题目】215. 数组中的第K个最大元素
在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
示例 1:
输入: [3,2,1,5,6,4] 和 k = 2
输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6] 和 k = 4
输出: 4
【解题思路1】
先排序,然后直接返回 nums.length-k 位置的元素就是第k大元素
class Solution {
public int findKthLargest(int[] nums, int k) {
//插入排序
for(int i=1; i<nums.length; i++){
int temp = nums[i]; //暂存要插入的元素
int j = i;
while( j>0 && temp<nums[j-1]){
nums[j] = nums[j-1]; //如果要插入的元素小于当前元素,当前元素后移
j--;
}
nums[j] = temp; //找到要插入的位置,插入元素
}
return nums[nums.length-k];
}
}
【解题思路2】小顶堆
建立小顶堆,并保持堆的大小<=k,这样最后堆顶就是第k大元素
动图演示可以参考官方题解
class Solution {
public int findKthLargest(int[] nums, int k) {
buildHeap(nums, k); //先用前k个元素原地建堆
for(int i=k; i<nums.length; i++){
if(nums[i] < nums[0]){
continue;
}
swap(nums, i, 0);
heapify(nums, k, 0);
}
return nums[0];
}
//建堆
public void buildHeap(int[] a, int k){
for(int i=k/2-1; i>=0; i--){
heapify(a, k, i);
}
}
public void heapify(int[] a, int k, int i){
int min = i;
while(true){
if(i*2+1<k && a[i*2+1]<a[i]){
min = i*2+1;
}
if(i*2+2<k && a[i*2+2]<a[min]){
min = i*2+2;
}
if(min==i){
break;
}
swap(a, i, min);
i = min;
}
}
public void swap(int[] a, int n, int m){
int temp = a[n];
a[n] = a[m];
a[m] = temp;
}
}
【解题思路3】快排 (待研究)
第k大元素也是第 N-K 小元素,利用快排的性质不断缩小范围
- 随机选择一个枢轴,使用划分算法将枢轴放在数组中的合适位置 pos。
- 将小于枢轴的元素移到左边,大于等于枢轴的元素移到右边。
- 比较 pos 和 N - k 以决定在哪边继续递归处理。
class Solution {
public int findKthLargest(int[] nums, int k) {
int size = nums.length;
// kth largest is (N - k)th smallest
return quickselect(nums, 0, size - 1, size - k);
}
public void swap(int[] nums, int a, int b) {
int tmp = nums[a];
nums[a] = nums[b];
nums[b] = tmp;
}
public int partition(int[] nums, int left, int right, int pivot_index) {
int pivot = nums[pivot_index];
// 1. move pivot to end
swap(nums, pivot_index, right);
int store_index = left;
// 2. move all smaller elements to the left
for (int i = left; i <= right; i++) {
if (nums[i] < pivot) {
swap(nums, store_index, i);
store_index++;
}
}
// 3. move pivot to its final place
swap(nums, store_index, right);
return store_index;
}
public int quickselect(int[] nums, int left, int right, int k_smallest) {
/*
Returns the k-th smallest element of list within left..right.
*/
if (left == right) // If the list contains only one element,
return nums[left]; // return that element
// select a random pivot_index
Random random_num = new Random();
int pivot_index = left + random_num.nextInt(right - left);
pivot_index = partition(nums, left, right, pivot_index);
// the pivot is on (N - k)th smallest position
if (k_smallest == pivot_index)
return nums[k_smallest];
// go left side
else if (k_smallest < pivot_index)
return quickselect(nums, left, pivot_index - 1, k_smallest);
// go right side
return quickselect(nums, pivot_index + 1, right, k_smallest);
}
}