获取一个数组里面第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)。