1. 程式人生 > >最快效率求出亂序數組中第k小的數

最快效率求出亂序數組中第k小的數

out 劃分 亂序 spa 雙向 ava 下標 sys ger

題目:以盡量高的效率求出一個亂序數組中按數值順序的第k 的元素值

思路:這裏很容易想到直接排序然後順序查找,可以使用效率較高的快排,但是它的時間復雜度是O(nlgn),我們這裏可以用一種簡便的方法,不一定需要排序,使用快速排序中雙向分區的掃描方法,掃描出主元下標,然後根據主元的值將數組劃分成一半大,一半小。然後再根據主元下標與k進行比較,如果相等,說明主元就是我們要找的數,如果大於k,說明k所代表的值在小的那邊,繼續向小的那部分遞歸,如果小於k,說明k代表的值在大的那邊,繼續向大的那部分遞歸。這樣即可得出正確答案。這種方法的時間復雜度為O(n),因為每次遞歸都相當於舍棄掉了差不多一半的數。

代碼:

import java.util.Arrays;

public class SelectK {

    public static void main(String[] args) {
        
        int arr[] = new int[10];
        for(int i=0;i<10;i++){
            arr[i] = (int) ((Math.random()+1)*10);
        }
        
        System.out.println("查找前數組:"+Arrays.toString(arr));
        
int k = selectK(arr, 0, arr.length-1, 5); System.out.println("查找出第k小的元素:"+k); } /** * * @param arr * @param p 開始下標 * @param r 結束下標 * @param k 求第k小元素 (遞增第k個元素) * @return */ public static int selectK(int[] arr,int p,int r,int k){
int q = partition2(arr, p, r); // 主元的下標 int qk = q - p + 1; // 主元是第幾個元素 if (qk==k) { return arr[q]; }else if (qk>k) { return selectK(arr, p, q-1, k); }else { return selectK(arr, q+1, r, k-qk); } } //雙向掃描分區法 public static int partition2(int[] arr, int p, int r) { int left = p + 1; //左側掃描指針 int right = r; //右側指針 int pivot = arr[p]; while(left <= right) { // left不停往右走,直到遇到大於主元的元素 // 循環退出時,left一定是指向第一個大於主元的位置 while(left <= right && arr[left] <= pivot) { left++; } // right不停往左走,直到遇到小於主元的元素 // 循環退出時,right一定是指向從右到左第一個小於於主元的位置 while(left <= right && arr[right] > pivot) { right--; } if(left < right) swap(arr, left, right); } // 循環退出時,兩者交錯,且right指向的最後一個小於等於主元的位置,也就是主元應該待的位置 swap(arr, p, right); return right; } private static void swap(int[] A, int p, int bigger) { int temp = A[p]; A[p] = A[bigger]; A[bigger] = temp; } }

結果:

  技術分享圖片

最快效率求出亂序數組中第k小的數