求陣列中第K大的數
阿新 • • 發佈:2019-01-26
本題的的陣列是可以包含重複元素的,且要求時間複雜度控制在O(n)
解題思路:陣列中第k大的數等價於排序陣列中第n-k個數,直觀的想法是將陣列排序後取第n-k個數即可,但是最快的排序演算法時間複雜度也是O(nlogn), 可以參考快速排序一次劃分的思想,將時間複雜度降低為O(n);一次劃分可以講陣列分為三部分,比支點元素小或者與支點元素相等的部分,支點元素,比支點元素的部分。一次partition之後,可以得到這個支點元素的下標,並且這個下標,也就是說這個數字在陣列中的位置已經確定,它就是第n-index大的數,因此我們可以在一次劃分後,比較由這次劃分所確定的元素的下標index和n-k比較,如果相等,則arr[index]就是第n-k個數;若index>
n - k,則證明第k大的數在 index的左邊,繼續在左邊進行一次劃分,;若index < n -k,則第k大的數在index的右邊,繼續在右邊進行一次劃分,直到使得index == n- k 為止。這裡運用了分治的思想。
public class Main { public static int findKthMax(int[] arr, int k) { if (arr == null || arr.length == 0 || k > arr.length) { return Integer.MIN_VALUE; } int length = arr.length; int start = 0; int end = length - 1; // 對陣列進行一次劃分 int index = partition(arr, start, end); //直到index為第n-k個數為止 while (index != length - k) { if (index > length - k) { //說明第length - k 個數在陣列的左邊,繼續在上次劃分的左邊裡尋找 end = index - 1; index = partition(arr, start, end); } else { //說明第length - k 個數在陣列的右邊,繼續在上次劃分的右邊尋找 start = index + 1; index = partition(arr, start, end); } } return arr[index]; } /** * @param arr * 帶劃分的陣列 * @param start * 陣列的起始位置 * @param end * 陣列的結束位置 return 返回陣列中某個元素的索引,索引左邊的元素都比該元素小或者相等,索引右邊的元素都比索引元素大 */ public static int partition(int[] arr, int start, int end) { if (arr == null || arr.length == 0 || start < 0 || start >= arr.length || end < 0 || end >= arr.length || start > end) { return -1; } // 將陣列的起始元素作為支點元素 int pivot = arr[start]; // left從陣列最左邊開始,往右移,直到找到一個比支點元素大的,待放入到陣列的右半部分 int left = start; // right從陣列的最右邊開始,往左移,直到找到一個比支點元素小的或者相等的,待放入到陣列的左半部分 // 那麼當left和right交叉時,right肯定是指向比支點元素小或者和支點元素想等的元素,此時right可以作為分界線 // right左邊都是比支點元素小的或者相等的,right右邊肯定是比支點元素大的 int right = end; while (left < right) { // left一直右移,直到找到一個比支點元素大的,待交換到陣列的右邊 while (arr[left] <= pivot) { left++; } // right一直左移,直到找到一個比支點元素小或者和支點元素相等的元素,待交換到陣列的左邊 while (arr[right] > pivot) { right--; } if (left < right) { swqp(arr, left, right); left++; right--; } } // 當left和right錯開後,此時已經將陣列分成兩個部分,right指向之前的包括right指向的元素都和支點元素相等或比支點元素小 // left指向之後的元素包括left指向元素都比支點元素大 // 將支點元素和right指向元素進行交換,right作為陣列的分割點 arr[start] = arr[right]; arr[right] = pivot; return right; } private static void swqp(int[] arr, int left, int right) { int temp = arr[left]; arr[left] = arr[right]; arr[right] = temp; } }