[剑指Offer]最小的K个数
来源:互联网 发布:淘宝什么时候能买彩票 编辑:程序博客网 时间:2024/06/15 12:47
题目描述
输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。
输入
int[] input:待搜索的数组
int k:需要找出最小的数量
输出
当k大于input长度时,返回空(非null)
当k小于input长度时,返回input中最小的k个数字
思路一
要求数组中最小的k个数,最容易想到的就是利用冒泡排序的思想,每一轮排序把剩余数组中最小的一个数字放到前面已排序的后面,只要进行K轮即可。代码如下:
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { if(input == null) return null; ArrayList<Integer> list = new ArrayList<Integer>(k); if(k > input.length) return list; int temp; for(int i = 0; i < k; i++){ for(int j = i + 1; j < input.length; j++){ if(input[i] > input[j]){ temp = input[i]; input[i] = input[j]; input[j] = temp; } } list.add(input[i]); } return list; }
该方法的优点是,思路清晰,易于理解,时间复杂度为O(kn)。缺点就是,时间复杂度大了点,关键是改变了原有的输入数组。
思路二
- 利用快速排序划分的思想,每一次划分就会有一个数字位于以数组从小到达排列的的最终位置index;
- 位于index左边的数字都小于index对应的值,右边都大于index指向的值;
- 所以,当index > k-1时,表示k个最小数字一定在index的左边,此时,只需要对index的左边进行划分即可;
- 当index < k - 1时,说明index及index左边数字还没能满足k个数字,需要继续对k右边进行划分;
代码如下:
public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) { if(input == null) return null; ArrayList<Integer> list = new ArrayList<Integer>(k); if(k > input.length) return list; int low = 0; int high = input.length - 1; int index = partition(input,low,high); while(index != k-1){ if(index > k-1){ high = index - 1; }else{ low = index + 1; } index = partition(input,low,high); } for(int i = 0; i < k; i++){ list.add(input[i]); } return list; } //划分操作 public int partition(int[] array,int start,int end){ int pivot = array[start]; while(start < end){ while(start < end && array[end] >= pivot) end--; array[start] = array[end]; while(start < end && array[start] <= pivot) start++; array[end] = array[start]; } array[start] = pivot; return start; }
这种方式的优点是时间复杂度较小为O(n),缺点就是需要修改输入数组。当我们不能对原有输入数组进行改变的时候,可以使用如下思路。
思路三
- 可以先创建一个大小为k的数据容器来存储最小的k个数字,从输入的n个整数中一个一个读入放入该容器中,如果容器中的数字少于k个,按题目要求直接返回空;
- 如果容器中已有k个数字,而数组中还有值未加入,此时就不能直接插入了,而需要替换容器中的值。按以下步骤进行插入:
- 先找到容器中的最大值;
- 将待查入值和最大值比较,如果待查入值大于容器中的最大值,则直接舍弃这个待查入值即可;如果待查入值小于容器中的最小值,则用这个待查入值替换掉容器中的最大值;
- 重复上述步骤,容器中最后就是整个数组的最小k个数字。
对于这个容器的实现,我们可以使用最大堆的数据结构,最大堆中,根节点的值大于它的子树中的任意节点值。Java中的TreeSet类实现了红黑树的功能,它底层是通过TreeMap实现的,TreeSet中的数据会按照插入数据自动升序排列(按自然顺序)。因此我们直接将数据依次放入到TreeSet中,数组就会自动排序。
代码如下:
public static ArrayList<Integer> GetLeastNumbers_Solution3(int [] input, int k) { if(input == null) return null; ArrayList<Integer> list = new ArrayList<Integer>(k); if(k > input.length) return list; TreeSet<Integer> tree = new TreeSet<Integer>(); for(int i = 0 ; i < input.length; i++){ tree.add(input[i]); } int i = 0; for(Integer elem : tree){ if(i >= k) break; list.add(elem); i++; } return list; }
时间复杂度为O(nlogn),优点:
1. 不会改变原来数组;
2. 这种思想,适合处理海量数据,特别是n大k小的情况。在处理海量数据的时候,受内存限制,数据可能不能一次全部读入内存,此时用这种方式也很好处理,只要想每次读入一些数据,与我们的容器中最大值比较,看是否需要进行替换操作。
缺点就是:
TreeSet不允许重复数据,因为TreeSet的底层是TreeMap实现,是将TreeSet添加的内容作为TreeMap的key值来存储,也就不能存在重复数据。由于这种限制,这就对我们的输入数组有要求,但我们可以通过自己实现最大堆或优化TreeSet来实现兼容存在重复数字的情况。
- 剑指offer:最小的k个数
- 【剑指offer】最小的k个数
- 剑指offer-30:最小的k个数
- 最小的k个数(剑指offer)
- 剑指offer--最小的K个数
- 剑指Offer之 - 最小的k个数
- 剑指offer-30 最小的K个数
- 剑指offer之最小的k个数
- 剑指offer 30 - 最小的k个数
- 《剑指offer》最小的K个数
- 【剑指Offer】最小的K个数
- 剑指offer—最小的K个数
- 剑指Offer-30-最小的k个数
- 剑指offer:最小的K个数
- 【剑指offer】之最小的k个数
- [剑指offer:30]最小的k个数
- 剑指offer 29 最小的k个数
- 剑指offer 最小的K个数
- Android 开源库
- 矩形嵌套
- Android Xutils 框架
- tornado中使用celery实现异步MySQL操作
- 关于使用thumbnailator生成缩略图异常问题
- [剑指Offer]最小的K个数
- 欢迎使用CSDN-markdown编辑器
- cVim—Chrome上更强大的vim插件
- Spring中遇到的问题及解决方法
- Android 学习笔记(11)—— ListView 实现活动跳转
- Java语法问题,构造函数为什么后面还有大括号?
- wordpress 安装 WP Super Cache
- Coursera Stanford 机器学习 笔记文章链接
- OpenVirteX架构研究(二)