欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

获取一个数组里面第K大的元素

程序员文章站 2022-06-23 09:14:14
如何在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代码实现 ......

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