面试题30:最小的k个数
2015-07-05 10:38
344 查看
题目:输入n个整数,找出其中最小的k个数。例如输入4、5、1、6、2、7、3、8这8个数字,则最小的
4个数字是1、2、3、4。
解法一:O(N)的算法,只有当可以修改输入的数组时可以用
参考面试题29“数组中出现次数超过一半的数字”,同样用partition函数来解决这个问题。如果partition函数的返回值
为k-1,则说明最左边的k个数就是最小的k个数。
解法二:O(NlogK)的算法,特别适合处理海量数据
创建一个大小为k的数据容器来存储最小的k个数字。每次从输入的n个整数中读入一个数,如果容器未满,则将它插入到存放k个数字的容器中。如果容器已满,并且当前数字小于容器中的最大数字,则将容器中的最大数字删除并插入当前数字。如果容器已满,并且当前数字大于容器中的最大数字,则抛弃当前数字。
因此,当容器满了之后,我们要做3件事情:
1.在k个整数中找到最大数;
2.有可能在这个容器中删除最大数;
3.有可能要插入一个新的数字;
如果用一个二叉树来实现这个数据容器,那么总能在O(logk)中实现这三步操作。因此总的时间复杂度为O(NlogK)。
我们可以选择不同的二叉树来实现这个数据容器,最大堆实现较为复杂,因此这里可以选择红黑树。STL中set和multiset都是基于红黑树实现的。考虑到可能有重复的数字,这里采用multiset实现。
第二种解法虽然相对第一种解法要慢,但它有两个明显的优点:1.没有修改输入的数据;2.适合海量数据的输入。如果要求从海量数据中找出最小的k个数,由于内存有限,不能将海量数据一次性读入内存,这时可以从辅助存储空间(如硬盘)一次读取一个数据进行处理。
解法三:利用multiset实现,时间复杂度和空间复杂度都为O(N)
4个数字是1、2、3、4。
解法一:O(N)的算法,只有当可以修改输入的数组时可以用
参考面试题29“数组中出现次数超过一半的数字”,同样用partition函数来解决这个问题。如果partition函数的返回值
为k-1,则说明最左边的k个数就是最小的k个数。
解法二:O(NlogK)的算法,特别适合处理海量数据
创建一个大小为k的数据容器来存储最小的k个数字。每次从输入的n个整数中读入一个数,如果容器未满,则将它插入到存放k个数字的容器中。如果容器已满,并且当前数字小于容器中的最大数字,则将容器中的最大数字删除并插入当前数字。如果容器已满,并且当前数字大于容器中的最大数字,则抛弃当前数字。
因此,当容器满了之后,我们要做3件事情:
1.在k个整数中找到最大数;
2.有可能在这个容器中删除最大数;
3.有可能要插入一个新的数字;
如果用一个二叉树来实现这个数据容器,那么总能在O(logk)中实现这三步操作。因此总的时间复杂度为O(NlogK)。
我们可以选择不同的二叉树来实现这个数据容器,最大堆实现较为复杂,因此这里可以选择红黑树。STL中set和multiset都是基于红黑树实现的。考虑到可能有重复的数字,这里采用multiset实现。
typedef multiset<int, greater<int> > intSet; typedef multiset<int, greater<int> > ::iterator setIterator; void getLeastNumbers(const vector<int>&data, intSet& leastNumbers, int k) { leastNumbers.clear(); if(k<1 || data.size()<k) return; vector<int>::const_iterator iter = data.begin(); for (; iter != data.end(); ++iter) { if ((leastNumbers.size()) < k) leastNumbers.insert(*iter); else { setIterator iterGreatest = leastNumbers.begin(); if (*iter < *(leastNumbers.begin())) { leastNumbers.erase(iterGreatest)); leastNumbers.insert(*iter); } } } }
第二种解法虽然相对第一种解法要慢,但它有两个明显的优点:1.没有修改输入的数据;2.适合海量数据的输入。如果要求从海量数据中找出最小的k个数,由于内存有限,不能将海量数据一次性读入内存,这时可以从辅助存储空间(如硬盘)一次读取一个数据进行处理。
解法三:利用multiset实现,时间复杂度和空间复杂度都为O(N)
vector<int> getLeastNumbers(vector<int>&data, int k) { int n = data.size(); vector<int>result; if (k<1 || n==0 || k > n) return result; multiset<int>m; for (int i = 0; i < n; ++i) { m.insert(data[i]); } multiset<int>::iterator it = m.begin(); for (int i = 0; i < k; ++i) { result.push_back(*it); it++; } return result; }
相关文章推荐
- 为什么中国的程序员总被称为码农?
- PHP面试题遇到的几个坑。...面壁ing
- 黑马程序员——指针的应用
- 揭秘程序员大脑编程的七大“误区
- 程序员节,你造吗?
- 程序员的工资有多高?你是否该跳槽了!
- 近一个月的面试总结
- 黑马程序员----继承3(内部类、异常、包)
- 黑马程序员——OC基础学习(六)---Foundation框架的常用类
- 【剑指Offer学习】【面试题37:两个链表的第一个公共结点】
- 【剑指Offer学习】【面试题36:数组中的逆序对】
- 【剑指Offer学习】【面试题35:第一个只出现一次的字符】
- 摘录-IT企业必读的200个.NET面试题-07 .NET多线程编程
- 黑马程序员——Java基础--IO流(一)
- #程序员健康 如何解救你的鼠标手
- IOS面试题(五)
- 程序员,你真的懂得收发电子邮件吗?(转)
- 程序员工作中会遭遇的天花板 工作中不由你控制的一些地方(转)
- iOS面试题
- 以我在LinkedIn做面试官的经验,来给程序猿面试提些建议