獲取一個數組里面第K大的元素


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM