studying
人心最苦处,最是拖泥带水

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

posted on 2019-01-29 23:49  小白一只726  阅读(1167)  评论(2编辑  收藏  举报