40 最小的K个数(时间效率)
题目描述:
输入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个数(时间效率)的更多相关文章
- 剑指 Offer 40. 最小的k个数 + 优先队列 + 堆 + 快速排序
剑指 Offer 40. 最小的k个数 Offer_40 题目描述 解法一:排序后取前k个数 /** * 题目描述:输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7. ...
- 剑指 Offer 40. 最小的k个数
剑指 Offer 40. 最小的k个数 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字,则最小的4个数字是1.2.3.4. 示例 1: 输入:ar ...
- 每日一题 - 剑指 Offer 40. 最小的k个数
题目信息 时间: 2019-06-30 题目链接:Leetcode tag: 快排 难易程度:中等 题目描述: 输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3. ...
- 【Java】 剑指offer(40) 最小的k个数
本文参考自<剑指offer>一书,代码采用Java语言. 更多:<剑指Offer>Java实现合集 题目 输入n个整数,找出其中最小的k个数.例如输入4.5.1.6.2.7 ...
- 剑指offer-最小的K个数-时间效率-排序-python
题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. 这就是排序题(将结果的最小K值输出) # -*- coding ...
- 剑指offer 面试题40. 最小的k个数
O(N)划分法,注意这个方法会改变原数据(函数参数是引用的情况下)!当然也可以再定义一个新容器对其划分 要求前k小的数,只要执行快排划分,每次划分都会把数据分成大小两拨.直到某一次划分的中心点正好在k ...
- 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 = ...
- 《剑指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 ...
- 1046: 最小的K个数
1046: 最小的K个数 时间限制: 1 Sec 内存限制: 128 MB提交: 233 解决: 200[提交][状态][讨论版] 题目描述 输入n个整数,找出其中最小的K个数.例如输入4,5,1 ...
随机推荐
- vs code--使用教程
这是Cnode论坛上的i5ting写的,很详细 教程地址 这个是微软官方的英文版的教程,这个更详细一些 微软官方vs code教程
- SSL延迟
原文链接 据说,Netscape公司当年设计SSL协议的时候,有人提过,将互联网所有链接都变成HTTPs开头的加密链接. 这个建议没有得到采纳,原因之一是HTTPs链接比不加密的HTTP链接慢很多.( ...
- 洛谷—— P1962 斐波那契数列
https://www.luogu.org/problem/show?pid=1962 题目背景 大家都知道,斐波那契数列是满足如下性质的一个数列: • f(1) = 1 • f(2) = 1 • f ...
- CentOS6.3安装Mysql-5.5.29
转自:http://www.cnblogs.com/zhoulf/archive/2013/01/25/zhoulf.html 安装方式分为rpm和源码编译安装两种,本文是采用mysql源码编译方式, ...
- jquery非文本框复制
function selectText(x) { if (document.selection) { var range = document.body.createTextRange();//ie ...
- Linux命令(八)——vi编辑器的使用
vi编辑器是linux系统下的标准正文编辑器,有三种基本模式:命令行模式.插入模式和底行命令模式. 1.命令行模式:控制屏幕光标的移动,字符.字或行的删除,移动复制某区段及进入插入模式或底行命令模式下 ...
- 大数据DDos检测——DDos攻击本质上是时间序列数据,t+1时刻的数据特点和t时刻强相关,因此用HMM或者CRF来做检测是必然! 和一个句子的分词算法CRF没有区别!
DDos攻击本质上是时间序列数据,t+1时刻的数据特点和t时刻强相关,因此用HMM或者CRF来做检测是必然!——和一个句子的分词算法CRF没有区别!注:传统DDos检测直接基于IP数据发送流量来识别, ...
- [TB-Technology] 淘宝在数据处理领域的项目及开源产品介绍
淘宝在数据存储和处理领域在国内互联网公司中一直保持比较靠前的位置,而且由于电子商务领域独特的应用场景,淘宝在数据实时性和大规模计算及挖掘方面一直在国内保持着领先,因此积累了很多的实践的经验和产品. T ...
- 关于ssh加密方式的理解
最近公司服务器被挖矿,所以更换了ssh的连接方式,从之前的密码登陆更换为密钥登陆方式,且禁止了密码登陆.所以在配置这个密钥的过程中,顺带了解了些ssh的原理和相关知识.通用的开源 1.ssh是什么,为 ...
- angular实现的tab栏切换
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...