版权声明:此文章为许诗宇所写,如需转载,请写下转载文章的地址 https://blog.csdn.net/xushiyu1996818/article/details/84950735
题目及测试
package pid215;
/*数组中的第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
说明:
你可以假设 k 总是有效的,且 1 ≤ k ≤ 数组的长度。
*/
public class main {
public static void main(String[] args) {
int[][] testTable = {{1,2,3,2},{1,2,3,4},{1,1,1,3,3,4,3,2,4,2},{3,2,1,5,6,4}};
for (int[] ito : testTable) {
test(ito,2);
}
}
private static void test(int[] ito,int k) {
Solution solution = new Solution();
int rtn;
long begin = System.currentTimeMillis();
for (int i = 0; i < ito.length; i++) {
System.out.print(ito[i]+" ");
}
System.out.println();
//开始时打印数组
rtn = solution.findKthLargest(ito,k);//执行程序
long end = System.currentTimeMillis();
//System.out.println(ito + ": rtn=" + rtn);
System.out.println(": rtn=" +rtn);
System.out.println();
System.out.println("耗时:" + (end - begin) + "ms");
System.out.println("-------------------");
}
}
解法1(成功,4ms,超快)
使用最小堆的方法,求第k大,建立一个规模为k的最小堆
首先将nums前k个元素加入堆,然后初始化最小堆,让heap[0]为这k个元素小的
然后将nums第k到length-1个,依次与heap[0]比较
如果比它大,则替代heap【0】,然后下沉,让heap[0]继续是这k个最小的
所以最后,heap为nums中前k大的,heap[0]为第k大的
该方法速度为o(nlogk),比全部排序o(nlogn)快
package pid215;
import java.util.Arrays;
import javax.naming.InitialContext;
public class Solution {
public int findKthLargest(int[] nums, int k) {
int length=nums.length;
int[] heap=new int[k];
for(int i=0;i<k;i++){
heap[i]=nums[i];
}
//先建立一个最小堆
for(int i=k-1;i>=0;i--){
upAdjust(heap,i,k);
}
//将其他的数与最小堆的顶部比较,比它大则替代顶部,然后下沉
for(int i=k;i<length;i++){
int now=nums[i];
if(now<=heap[0]){
continue;
}
else{
heap[0]=now;
downAdjust(heap, 0, k);
}
}
return heap[0];
}
//调整i位和它的子节点的大小,使小的上浮
public static void upAdjust(int[] nums,int i,int length){
int left=2*i+1;
int right=2*i+2;
if(left<length&&nums[left]<nums[i]){
swap(nums, i, left);
}
if(right<length&&nums[right]<nums[i]){
swap(nums, i, right);
}
if(left<length){
upCheck(nums, left, length);
}
if(right<length){
upCheck(nums, right, length);
}
}
public static void upCheck(int[] nums,int i,int length){
int left=2*i+1;
int right=2*i+2;
if((left<length&&nums[left]<nums[i])||(right<length&&nums[right]<nums[i])){
upAdjust(nums, i, length);
}
}
public static void downAdjust(int[] nums,int i,int length){
int left=2*i+1;
int right=2*i+2;
if(left<length&&nums[left]<nums[i]&&(right>=length||(right<length&&nums[left]<=nums[right]))){
swap(nums, i, left);
downAdjust(nums, left, length);
}
if(right<length&&nums[right]<nums[i]&&nums[left]>nums[right]){
swap(nums, i, right);
downAdjust(nums, right, length);
}
}
public static void swap(int[] nums,int i,int j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
解法2(别人的)
利用快速排序的思想,在进行排序过程中每次可以确定一个元素的最终位置,若此位置为第K个最大元素,则直接返回此索引,否则继续分治进行快速排序。不用排列全部,只要每次只排序一段,找到即可
class Solution {
public int findKthLargest(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];
}
public 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可以得到划分的这个数的下标
}
private void swap(int[] a, int i, int j) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}