獲取一個陣列裡面第K大的元素

小白一隻726發表於2019-01-29

如何在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)。

相關文章