题目描述:

输入n个整数,找出其中最小的K个数。例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,。

 

测试用例:

功能测试(输入的数组中有相同的数字;输入的数组中没有相同的数字)

边界值测试(输入的k等于1或者等于数组的长度)

特殊输入测试(k小于1;k大于数组的长度;指向数组的指针为NULL)

解题思路:

1)把数组排序后,前面的k个数就是最小的k个数。时间复杂度为O(nlogn)  面试官会提示,使用更快的方法。


2)当可以修改数组时,基于Partition函数的O(n)方法

基于Parition函数:基于数组中的第k个数字来调整,使得比第k个数字小的所有数字都位于数组的左边,比第k个数字大的都位于数组的右边。调整之后位于左边的k个数字就是最小的k个数字(这k个数字不一定是排序的)

class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
//特殊输入判断
vector<int> res;
if(input.empty() || k<1 || k>input.size()) //不要忘记考虑k比数组长度大的情况!!! 对每个输入参数考虑非法输入
return res; int begin = 0;
int end = input.size()-1;
//基于Partition
int index = Partition(input, begin, end);
while(index!=k-1){ //第k个元素,索引为k-1
if(index>k-1){
end = index-1;
index = Partition(input, begin, end);
}else{
begin = index+1;
index = Partition(input, begin, end);
}
}
for(index=0;index<k;index++){
res.push_back(input[index]);
} return res;
} int Partition (vector<int> &input, int begin,int end){
if(end<begin ||begin<0 ||end>input.size()-1 || input.empty())
return -1; int index = RandomInRange(begin,end);
swap(input[index],input[end]); //把选中的元素放到最后
int small = begin-1;
for(index=begin;index<end;index++){
if(input[index]<input[end]){
small++;
if(small!=index)
swap(input[index],input[small]);
}
}
small++;
swap(input[end],input[small]);
return small;
} int RandomInRange(int begin,int end){
return (rand() %(end-begin+1) + begin);
}
};  

代码注意:

-1. 最小的第k个元素,对应的索引是k-1

-2. 特殊输入的判断不要忘记考虑k比数组长度大的情况

 k>input.size()

3)时间复杂度为O(nlogk)的算法,特别适合处理海量数据

思路:定义一个大小为k的数据容器来存储k个数,容器未满直接向容器中添加数字,容器已满,找出k个数字的最大值,然后与待插入的整数比较,如果待插入的值比当前已有的最大值小,则用这个数替换当前已有的最大值;如果待插入的值比当前最大值还要大,那么这个数不可能是最小的k个整数之一,抛弃这个整数。

容器满3个操作:1.找到最大值 2.删除最大值 3.插入最大值 用二叉树来实现这个容器,可以在O(logk)时间内实现这三个操作。因此对于N个输入的数字而言总的时间效率就是O(nlogk)

可以使用的数据结构:最大堆 或 红黑树

最大堆中,根节点的值总是大于它的子树中的任意节点的值。可以每次在O(1)时间内得到已有的k个数字中的最大值,但需要O(logk)时间完成删除以及插入操作。

红黑树通过把节点分为红、黑两种颜色并根据一些规则确保树在一定程度上是平衡的,从而保证在红黑树中的查找、删除和插入操作都只需要O(logk)时间。

在STL中的数据容器,set和multiset都是基于红黑树实现的。

实现1,使用升序数组

class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
//特殊输入判断
if(input.empty() || k<1 || k>input.size()) //不要忘记考虑k比数组长度大的情况!!! 对每个输入参数考虑非法输入
return vector<int>(); typedef multiset<int,greater<int>> intSet;
//typedef multiset<int,greater<int>>::iterator setIterator;
intSet leastNumbers;
//比较好的方法是for循环时,定义常量迭代器
//vector<int>::const_iterator iter = input.begin();
for(auto iter = input.begin();iter!=input.end();iter++){ //遍历每一个元素
if(leastNumbers.size()<k)
leastNumbers.insert(*iter);
else{
if(*iter<*(leastNumbers.begin())) {//获取set的首元素,要解引用*(leastNumbers.begin())
//删掉首元素,并添加新的元素
leastNumbers.erase(leastNumbers.begin());
leastNumbers.insert(*iter);
}
}
} return vector<int>(leastNumbers.begin(),leastNumbers.end());
}
};  

代码注意:

1. greater<int> 在库文件 #include<functional>中。表示内置类型从大到小排序。  less<int> 内置类型从小到大排序

2. 对迭代器解引用才是对应的值内容

实现2:使用降序数组

class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
//特殊输入判断
if(input.empty() || k<1 || k>input.size()) //不要忘记考虑k比数组长度大的情况!!! 对每个输入参数考虑非法输入
return vector<int>(); typedef multiset<int> intSet;
typedef multiset<int,greater<int>>::iterator setIterator;
intSet leastNumbers;
//比较好的方法是for循环时,定义常量迭代器
//vector<int>::const_iterator iter = input.begin();
for(auto iter = input.begin();iter!=input.end();iter++){ //遍历每一个元素
if(leastNumbers.size()<k)
leastNumbers.insert(*iter);
else{
setIterator last = --leastNumbers.end();
if(*iter<*(last)) {//获取set的首元素,要解引用*(leastNumbers.begin())
//删掉首元素,并添加新的元素
leastNumbers.erase(last);
leastNumbers.insert(*iter);
}
}
} return vector<int>(leastNumbers.begin(),leastNumbers.end());
}
};  

注意:

对迭代器iter

*(iter++)  对iter解引用,然后将iter向后移动一位

(*iter)++    对iter解引用,然后对解引用后的内容++

*(++iter)  将iter向后移动一位,然后对当前位置(移动后的迭代器位置)解引用

*iter++  对iter解引用,然后将iter向后移动一位


4)最大堆 时间复杂度O(nlogk)(时间复杂度不确定)

class Solution {
public:
vector<int> GetLeastNumbers_Solution(vector<int> input, int k) {
int len=input.size();
if(len<=0 || k>len || k<=0) return vector<int>(); //注意是k<=0;没有等号的话,程序不通过 vector<int> res(input.begin(),input.begin()+k);
//建堆
make_heap(res.begin(),res.end()); for(int i=k;i<len;i++)
{
if(input[i]<res[0])
{
//先pop,然后在容器中删除
pop_heap(res.begin(),res.end());
res.pop_back();
//先在容器中加入,再push
res.push_back(input[i]);
push_heap(res.begin(),res.end());
}
}
//使其从小到大输出
sort_heap(res.begin(),res.end()); return res;
}
};  

代码注意:

  • make_heap()生成堆,他有两个参数,也可以有三个参数,前两个参数是指向开始元素的迭代器和指向结束元素的下一个元素的迭代器。第三个参数是可选的,可以用伪函数less()和greater()来生成大顶堆和小顶堆,其中type为元素类型。如果只传入前两个参数,默认是生成大顶堆。 [first,last)     这个区间是半开半闭的。
  • push_heap()是在堆的基础上进行数据的插入操作,参数与make_heap()相同,需要注意的是,只有make_heap()和push_heap()同为大顶堆或小顶堆,才能插入。执行push_heap 时, [first,last-1)个元素是保持堆形态的,如果不是堆,则会报错。
  • pop_heap()是在堆的基础上,弹出堆顶元素。这里需要注意的是,pop_heap()并没有删除元素,而是将堆顶元素和数组最后一个元素进行了替换,如果要删除这个元素,还需要对数组进行pop_back()操作。 使用pop_heap 操作后, 最大值被移动到last-1的位置。[first ,last-1) 之间的元素继续保持堆的形态。

40 最小的K个数(时间效率)的更多相关文章

  1. 剑指 Offer 40. 最小的k个数 + 优先队列 + 堆 + 快速排序

    剑指 Offer 40. 最小的k个数 Offer_40 题目描述 解法一:排序后取前k个数 /** * 题目描述:输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7. ...

  2. 剑指 Offer 40. 最小的k个数

    剑指 Offer 40. 最小的k个数 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:ar ...

  3. 每日一题 - 剑指 Offer 40. 最小的k个数

    题目信息 时间: 2019-06-30 题目链接:Leetcode tag: 快排 难易程度:中等 题目描述: 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3. ...

  4. 【Java】 剑指offer(40) 最小的k个数

    本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集   题目 输入n个整数,找出其中最小的k个数.例如输入4.5.1.6.2.7 ...

  5. 剑指offer-最小的K个数-时间效率-排序-python

    题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 这就是排序题(将结果的最小K值输出)   # -*- coding ...

  6. 剑指offer 面试题40. 最小的k个数

    O(N)划分法,注意这个方法会改变原数据(函数参数是引用的情况下)!当然也可以再定义一个新容器对其划分 要求前k小的数,只要执行快排划分,每次划分都会把数据分成大小两拨.直到某一次划分的中心点正好在k ...

  7. leetcode 签到 面试题40. 最小的k个数

    题目 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k = ...

  8. 《剑指offer》面试题40. 最小的k个数

    问题描述 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k ...

  9. 1046: 最小的K个数

    1046: 最小的K个数 时间限制: 1 Sec  内存限制: 128 MB提交: 233  解决: 200[提交][状态][讨论版] 题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1 ...

随机推荐

  1. 0120Keeplived实现自动切换Mysql服务

    转自http://biancheng.dnbcw.info/mysql/381020.html Keepalived+mysql 自动切换网络结构:VIP 192.168.88.200mysq11 1 ...

  2. uva:10763 - Foreign Exchange(排序)

    题目:10763 - Foreign Exchange 题目大意:给出每一个同学想要的交换坐标 a, b 代表这位同学在位置a希望能和b位置的同学交换.要求每一位同学都能找到和他交换的交换生. 解题思 ...

  3. iOS 在 ARC 环境下 dealloc 的使用、理解误区

    iOS 在 ARC 环境下 dealloc 的使用.理解误区 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致&qu ...

  4. 新手git: ssh: connect to host localhost port 22: Connection refused

    由于gitlab上要git pull或者git clone,可是每次都出现这个问题.之前偶尔出现这个问题.可是仅仅是偶尔.这是为什么呢?然后就開始搜索网上的解决方式了. 这个问题搜索网上非常多答案.可 ...

  5. 8 Reasons why SharePoint is Bad for Your Business 8个理由告诉你,为什么SharePoint对你的业务有害

    8 Reasons why SharePoint is Bad for Your Business 8个理由告诉你,为什么SharePoint对你的业务有害         SharePoint近期已 ...

  6. springmvc and maven

    使用Maven构建Spring MVC项目的简单示例 标签: mavenspringmvcspring 2013-09-29 12:40 42823人阅读 评论(8) 收藏 举报  分类: Maven ...

  7. 自己实现android側滑菜单

    当今的android应用设计中.一种主流的设计方式就是会拥有一个側滑菜单,以图为证:     实现这种側滑效果,在5.0曾经我们用的最多的就是SlidingMenu这个开源框架,而5.0之后.goog ...

  8. Linux下开启vim高亮

    默认是不高亮的. [root@local ~]# vi ~/.vimrc 没有则新建这个文件. 或者修改 [root@local vim74]# vi /etc/vimrc 添加一行. syntax ...

  9. Intellij IDEA社区版打包Maven项目成war包,并部署到tomcat上

    转自:https://blog.csdn.net/yums467/article/details/51660683 需求分析 我们利用 Intellij idea社区版IDE开发了一个maven的sp ...

  10. php json 初始化函数(格式化json字符串为php json_decode 标准的字符串)

    $json="[{ 'i':100000, 'u':-1,n: '中国'},{i:100001,u:-1,n:'阿尔巴尼亚'},{i:100002,u:-1,n:'阿尔及利亚',}]&quo ...