如何在O(n)內獲取一個陣列比如{9, 1, 2, 8, 7, 3, 6, 4, 3, 5, 0, 9, 19, 39, 25, 34, 17, 24, 23, 34, 20}裡面第K大的元素呢?
我們可以使用類似快排的分割槽方式,將第K大的元素限定在陣列的左邊或右邊,遞迴求取。
我的Java程式碼實現如下:
1 package com.structure.sort; 2 3 /** 4 * @author zhangxingrui 5 * @create 2019-01-27 22:52 6 **/ 7 public class QuickSort { 8 9 public static void main(String[] args) { 10 int[] numbers = {9, 1, 2, 8, 7, 3, 6, 4, 3, 5, 0, 9, 19, 39, 25, 34, 17, 24, 23, 34, 20}; 11 // int[] numbers = {3,1,2}; 12 // 快速排序藉助遞迴來實現,重要的是要找到遞迴的終結條件(不然容易發生堆疊異常) 13 // 遞推公式:quickSort(p...r) = merge(p, q - 1) + merge(q+1, r) 14 // 終結條件:p >= r 15 /*quickSort(numbers, 0, numbers.length - 1); 16 for (int number : numbers) { 17 System.out.println(number); 18 }*/ 19 20 int k = getK(4, numbers, 0, numbers.length - 1); 21 System.out.println(k); 22 } 23 24 private static void quickSort(int[] numbers, int p, int r){ 25 if(p >= r) 26 return; 27 int q = partition(numbers, p, r, false); 28 quickSort(numbers, p, q - 1); 29 quickSort(numbers, q + 1, r); 30 } 31 32 /** 33 * @Author: xingrui 34 * @Description: 分割槽 35 * @Date: 23:13 2019/1/27 36 */ 37 private static int partition(int[] numbers, int p, int r, boolean isAsc){ 38 int k = numbers[r]; 39 int i = p; 40 41 if(isAsc){ 42 for (int j = p; j <= r; ++j) { 43 if(numbers[j] < k){ 44 int temp = numbers[i]; 45 numbers[i] = numbers[j]; 46 numbers[j] = temp; 47 i++; 48 } 49 } 50 numbers[r] = numbers[i]; 51 numbers[i] = k; 52 return i; 53 }else{ 54 for (int j = p; j <= r; ++j) { 55 if(numbers[j] > k){ 56 int temp = numbers[i]; 57 numbers[i] = numbers[j]; 58 numbers[j] = temp; 59 i++; 60 } 61 } 62 numbers[r] = numbers[i]; 63 numbers[i] = k; 64 return i; 65 } 66 67 } 68 69 /** 70 * @Author: xingrui 71 * @Description: 獲取第K大的元素 72 * @Date: 23:15 2019/1/29 73 */ 74 private static int getK(int k, int[] numbers, int p, int r){ 75 int q = partition(numbers, p, r, false); 76 77 if(q + 1 == k) 78 return numbers[q]; 79 80 if(q + 1 > k){ 81 return getK(k, numbers, p, q - 1); 82 }else{ 83 return getK(k, numbers, q + 1, r); 84 } 85 } 86 87 }
原理就是我們先任取一個數作為分割槽的數,把大於它的數放在它的左邊,小於它的數放在它的右邊。
假設我們有陣列array[p…r],那麼第一次分割槽之後就形成了array[p…q-1],q,array[q+1…r]三個部分,如果我們要求取第3大的元素,
那麼就將3與q+1做比較,
如果3==q+1,那麼說明array[p…q-1]裡面只有兩個元素且都>array[q],而array[q+1,r]都<q,所以array[q]
就是第三大的元素;
如果3 > q+1,說明array[p…q-1]裡面的元素只有一個元素,所以我們需要到array[q+1…r]裡面再去找;
如果3 < q+1,則說明array[p…q-1]裡面有三個元素,所以我們還需要到array[p…q-1]裡面去找。
這樣的話,每次只會到分割槽的一半的陣列裡面去找:n/2 + n/4 + n/8 + 直到區間縮小為1,最終可得2n – 1,
所以這樣做的時間複雜的就是O(n)。