1. 程式人生 > >leetcode-215-陣列中的第K個最大元素 (kth largest element in an array)-java

leetcode-215-陣列中的第K個最大元素 (kth largest element in an array)-java

題目及測試

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;
    }
}