leetcode-215-陣列中的第K個最大元素 (kth largest element in an array)-java
阿新 • • 發佈:2018-12-29
題目及測試
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; } }