给定一个数组,求前k小或者前k大。

2024-03-18 04:18
文章标签 数组 给定 求前

本文主要是介绍给定一个数组,求前k小或者前k大。,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载地址:http://blog.csdn.net/jeffleo/article/details/64133292

问题

面试常考的问题,给定一个数组,求前k小或者前k大。 
解法: 
1. 快速排序 
2. 堆排序 
3. 冒泡排序

解法(前k大和前k小思路相反,只说前k大情况)

1. 快速排序 近似O(n)

  1. 利用partition分割成两个数组left[] 和 right[]
  2. 如果此时分割点mid,小于k,说明left中都是前k大的,而且还要在right中取(k-mid)个数
  3. 如果mid大于k,说明前k大的数全部在left中,然后继续在left中找 
    ps:求前k小,则维持一个递增数列,求前k大,则维持一个递减数列
public class FastSortBeforeK {static int[] array = new int[]{100,20,4,2,87,9,8,5,46,26};public static void sort(int low, int high, int k){if(low < high){//先分出两个数组,mid为分割点int mid = partition(low, high);//左边的不够k个,还要在右边找if(mid < k){sort(mid+1, high, k-mid);//前k个全部在左边}else if(mid > k) {sort(low, mid-1, k);//刚好k个,直接输出}else{return;}}}public static int partition(int low, int high){int privoteKey = array[low];int back = privoteKey;while(low < high){//从右边数,大的全部放到左边while(low<high && array[high] <= privoteKey){high--;}//直到发现了有半部分有小于privotekey的数array[low] = array[high];//从左边数,小的全部放到右边while(low<high && array[low] >= privoteKey){low++;}array[high] = array[low];}array[low] = back;return low;}public static void swap(int i, int j){int tmp = array[i];array[i] = array[j];array[j] = tmp;}public static void main(String[] args){int k = 6;sort(0, array.length-1, k-1);for(int i = 0; i < k; i++){System.out.print(array[i] + " ");}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56

2. 堆排序 O(nlogk)

求前k大,就维持一个k大小的小根堆,求前k小,就维持一个k大小的大根堆。 
1. 由于我们是求前k大,所以我们构造一个小根堆 
2. 从k+1开始,跟堆顶比较,由于小根堆的堆顶是根堆中最小的,如果节点都小于堆顶,自然不可能是前k大的,所以不能加进来 
3. 如果大于堆顶,可以加进来 
4. 每加一个进来,就要重新调节堆,使堆顶是前k个最小的

public class BeforeKHeap {static int array[] = new int[]{0,50,10,90,30,70,40,80,60,20};static int size = 9;public void sort(int k){//k为4//1. 首先构造根堆,为什么i为k/2,因为构造堆从底向上,定位到2for(int i = k / 2; i >= 1; i--){heapAdjust(i, k);}//构造完后,此时[10 30 90 50 70 40 80 60 20 ],见下图for(int i = k+1; i <= size; i++){//如果小于堆顶if(array[i] > array[1]){swap(1, i);heapAdjust(1, k);}}}public  void heapAdjust(int root, int end){int temp = array[root];for(int i = root * 2; i <= end; i*=2){//这里不能<=,因为是为了维持k的根堆if(i < end && array[i] > array[i+1]){i++;}if(temp <= array[i]){break;}array[root] = array[i];root = i;}array[root] = temp;}public void swap(int i, int j){int temp = array[i];array[i] = array[j];array[j] = temp;}public static void main(String args[]){int k = 4;new BeforeKHeap().sort(k);for(int i = 1; i <= k; i++){System.out.print(array[i] + " ");}}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

步骤: 
1、原堆 
这里写图片描述 
2、构造k=4的小根堆 
这里写图片描述 
3、开始从k+1的节点和堆顶比较 
这里写图片描述 
4、交换后重新调整 
这里写图片描述 
5、然后比较40,80,60,20,最终形成的根堆,前k个就是最大的k个 
这里写图片描述

3. 冒泡排序 O(n*k)

只要冒到第k个就可以了,简单易理解

public class BeforeKMaopao {public void sort(int[] array, int k){//冒到k就跳出for(int i = 0; i < k; i++){for(int j = array.length - 1; j > i; j--){if(array[j] > array[j-1]){swap(array, j, j-1);}}}}public void swap(int[] array, int i, int j){int temp = array[i];array[i] = array[j];array[j] = temp;}public static void main(String[] args){BeforeKMaopao sort = new BeforeKMaopao();int[] array = new int[]{50,10,90,30,70,40,80,60,20};sort.sort(array, 4);for(int i = 0 ; i < 4; i++){System.out.print(array[i] + " ");}}
}

这篇关于给定一个数组,求前k小或者前k大。的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/821149

相关文章

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

shell编程之函数与数组的使用详解

《shell编程之函数与数组的使用详解》:本文主要介绍shell编程之函数与数组的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录shell函数函数的用法俩个数求和系统资源监控并报警函数函数变量的作用范围函数的参数递归函数shell数组获取数组的长度读取某下的

C++原地删除有序数组重复项的N种方法

《C++原地删除有序数组重复项的N种方法》给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素只出现一次,返回移除后数组的新长度,不要使用额外的数组空间,你必须在原地修改输入数组并在使用O(... 目录一、问题二、问题分析三、算法实现四、问题变体:最多保留两次五、分析和代码实现5.1、问题分析5.

Java中数组转换为列表的两种实现方式(超简单)

《Java中数组转换为列表的两种实现方式(超简单)》本文介绍了在Java中将数组转换为列表的两种常见方法使用Arrays.asList和Java8的StreamAPI,Arrays.asList方法简... 目录1. 使用Java Collections框架(Arrays.asList)1.1 示例代码1.

C++一个数组赋值给另一个数组方式

《C++一个数组赋值给另一个数组方式》文章介绍了三种在C++中将一个数组赋值给另一个数组的方法:使用循环逐个元素赋值、使用标准库函数std::copy或std::memcpy以及使用标准库容器,每种方... 目录C++一个数组赋值给另一个数组循环遍历赋值使用标准库中的函数 std::copy 或 std::

C++初始化数组的几种常见方法(简单易懂)

《C++初始化数组的几种常见方法(简单易懂)》本文介绍了C++中数组的初始化方法,包括一维数组和二维数组的初始化,以及用new动态初始化数组,在C++11及以上版本中,还提供了使用std::array... 目录1、初始化一维数组1.1、使用列表初始化(推荐方式)1.2、初始化部分列表1.3、使用std::

C++ Primer 多维数组的使用

《C++Primer多维数组的使用》本文主要介绍了多维数组在C++语言中的定义、初始化、下标引用以及使用范围for语句处理多维数组的方法,具有一定的参考价值,感兴趣的可以了解一下... 目录多维数组多维数组的初始化多维数组的下标引用使用范围for语句处理多维数组指针和多维数组多维数组严格来说,C++语言没

Java 字符数组转字符串的常用方法

《Java字符数组转字符串的常用方法》文章总结了在Java中将字符数组转换为字符串的几种常用方法,包括使用String构造函数、String.valueOf()方法、StringBuilder以及A... 目录1. 使用String构造函数1.1 基本转换方法1.2 注意事项2. 使用String.valu

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE