一. 题目描述
寻找无序数组中的第k大元素
二. 思考与解法
1. 局部冒泡
针对这个问题,我们最直观的想法一般是排序。在排序后选取第k大的值。那么在运用排序算法解决这个的时候就不得不去考虑,排序算法的时间复杂度和空间复杂度。
所以针对这个问题,可以进行一些优化和思考。当K很小的时候,比如K=1。寻找最大的元素,那么一趟遍历便能找到结果。因此我们只要对冒泡排序稍做改动便可。
public static int FindValue(int[] array, int k){
//在第k次冒泡的时候终止
for(int i = 0; i < k; i++){
for(int j = 0; j < array.length-i-1; j++){
if (array[j] > array[j+1]){
int tmp = array[j];
array[j] = array[j+1];
array[j+1] = tmp;
}
}
}
return array[array.length-k];
}
冒泡排序的算法复杂度位:O(N^2),K冒泡的算法复杂度为KN,对比排序算法的最好情况时间复杂度O(NlogN),因此当 K<logN 时K冒泡会更有优势。
2. 小顶堆
同样我们也可以借助,堆排序的方法来解决这一问题。构造大小为K的小顶堆,那可堆顶就是第K大的值。在遍历时,只需对比堆顶大的值,进行入堆和调整。最后小顶堆的顶即为所求。
public static int FindValue(int[] array, int k){
Queue<Integer> heap = new PriorityQueue<>(k, new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1-o2;
}
}); //小顶堆
for(int i = 0; i < array.length; i++){
if(i<k){
heap.offer(array[i]);//前k个数,直接入堆
}else if(array[i] > heap.peek()){ //比小顶值大,则需要重新建堆
heap.poll();
heap.offer(array[i]);
}
}
return heap.peek();
}
3. 快排
快速排序每次至少能将一个数字放在其排序后的位置,其左边的数据是小于该数字的,右边数据是大于该数字的。没一趟快排能确定一次基准值,通过不断调整基准值,直至基准值等于N-K。此时N-K左侧的值小于它,右侧的值大于它。那么N-K对应的值便为第K大的值。
public static int FindValue(int[] nums, int k){
int begin=0;
int end=nums.length-1;
k=nums.length+1-k;
while(begin<end){
int pos=partition(nums,begin,end);
if(pos==k-1) {
break;
} else if(pos<k-1) {
begin=pos+1;
} else {
end=pos-1;
}
}
return nums[k-1];
}
private static void swap(int[] a, int i, int j){
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
private static int partition(int[] nums, int l, int r){
int less=l-1;//小于区的下标
int more=r;//大于区的下标,默认以最后一个下标的数作为划分值
while(l<more){
if(nums[l]<nums[r]) {
swap(nums,++less,l++);
} else if (nums[l]>nums[r]) {
swap(nums,--more,l);
} else {
l++;
}
}
swap(nums,more,r);
return less+1;//小于区位置+1可以得到划分的这个数的下标
}
4. 桶排序
求出数组中的最大最小值,以1为单位间隔画桶,遍历数组中的值放入对应的桶中。最后再查找第K大的值所对应的桶。
public static int FindValue(int[] nums, int k){
int max = Integer.MIN_VALUE;
int min = Integer.MAX_VALUE;
for(int num : nums){
max = Math.max(max, num);
min = Math.min(min, num);
}
int[] bucket = new int[max-min+1];
for(int i=0; i<nums.length; i++){
bucket[nums[i] - min]++;
}
int count = 0;
for(int j=bucket.length-1; j>=0; j--){
count += bucket[j];
if(count >= k) {
return j+min;
}
}
return -1;
}
三、总结
网上类似的总结很多,这里主要参考了这篇博客:
链接: https://blog.csdn.net/orangefly0214/article/details/86527462.