题目描述:

输入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. 洛谷—— P1097 统计数字

    https://www.luogu.org/problem/show?pid=1097 题目描述 某次科研调查时得到了n个自然数,每个数均不超过1500000000(1.5*10^9).已知不相同的数 ...

  2. oc08--局部变量,全局变量,函数方法的区别

    // // main.m // 局部变量和全局变量以及成员变量的区别 #import <Foundation/Foundation.h> @interface Person : NSObj ...

  3. hdoj--1045--Fire Net(二分图)

    Fire Net Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total S ...

  4. css3 背景background

    Css3背景<background> Css3包含多个新的背景属性,它们提供了对背景更强大的控制.可以自定义背景图的大小,可以规定背景图片的定位区域,css3还允许我们为元素使用多个背景图 ...

  5. 修改python的pip下载源

    推荐两个源: 豆瓣:http://pypi.douban.com/simple/ 清华:https://pypi.tuna.tsinghua.edu.cn/simple 使用方法有两种,一种为临时使用 ...

  6. Blender Python UV 学习

    Blender Python UV 学习 1. bmesh面转换 bm = bmesh.from_edit_mesh(bpy.context.edit_object.data) bm.faces.en ...

  7. D - Knight Tournament(set)

    Problem description Hooray! Berl II, the king of Berland is making a knight tournament. The king has ...

  8. 豆瓣项目(用react+webpack)

    用豆瓣电影api的项目 电影列表组件渲染 步骤: 1. 发送Ajax请求 1.1 在组件的componentWillMount这个生命周期钩子中发送请求 1.2 发送ajax XMLHttpReque ...

  9. fragment基础 fragment生命周期 兼容低版本

    fragment入门 ① 创建一个类继承Fragment 重写oncreateView方法 public class FirstFragment extends Fragment { @Overrid ...

  10. (转载) android studio library生成jar包和aar的方法总结

    android studio library生成jar包和aar的方法总结 标签: android学习文档jar和aar的使用与生成gradle 2016-11-25 10:39 1782人阅读 评论 ...