如何求出数组中最小(或者最大)的k个数(least k问题)
输入n个整数,如何求出其中最小的k个数?
解法1. 当然最直观的思路是将数组排序,然后就可以找出其中最小的k个数了,时间复杂度以快速排序为例,是O(nlogn);
解法2. 借助划分(Partition)的思路,一次划分可以把枢轴使得枢轴左边的元素都比枢轴小,枢轴右边的元素都比枢轴大(可以参考快速排序及STL中的sort算法)。那么可以基于数组的第k个数字来调整,使得比第k个数字小的数字都位于数组的左边,使得比第k个数字大的数字都位于数组的右边。那么调整完毕后,数组中左边的k个数字就是最小的k个数字(这k个数字不一定是排序的)。该解法时间复杂度最低,是O(n),但需要修改输入的数组,如果要求不能修改输入的数组,那就行不通了。另外该算法也不大适合海量数据处理,因为没办法一次读入内存,只能一次读取一些。
C++代码如下:
- #include "stdafx.h"
- #include <set>
- #include <vector>
- #include <iostream>
- using namespace std;
- int Partition(int data[], int length, int start, int end)
- {
- if(data == NULL || length <= || start < || end >= length)
- throw new std::exception("Invalid Parameters");
- int pivotkey = data[start];// 记录枢轴关键字
- while (start < end)
- {
- while(start<end && data[end]>=pivotkey)
- --end;// 找到从end位置开始向前第一个比枢轴小的元素
- data[start] = data[end];// 将找到的比枢轴小的元素放到前边的空闲位置
- while(start<end && data[start]<=pivotkey)
- ++start;// 找到从start位置开始向后第一个比枢轴大的元素
- data[end] = data[start];// 将找到的比枢轴大的元素放到后边的空闲位置
- }
- data[start] = pivotkey;// 将枢轴放回中间的空闲位置
- return start;
- }
- void GetLeastNumbers(int* input, int n, int* output, int k)
- {
- if(input == NULL || output == NULL || k > n || n <= || k <= )
- return;
- int start = ;
- int end = n - ;
- int index = Partition(input, n, start, end);
- while(index != k - )
- {
- if(index > k - )
- {
- end = index - ;
- index = Partition(input, n, start, end);
- }
- else
- {
- start = index + ;
- index = Partition(input, n, start, end);
- }
- }
- for(int i = ; i < k; ++i)
- output[i] = input[i];
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- int data[] = {, , , , , , , , , , , , , -};
- int n = ;
- int k = ;
- int* output = new int[k];
- GetLeastNumbers(data, n, output, k);
- for (int i=; i<n; i++)
- {
- printf("%d\t", data[i]);
- }
- printf("\r\n");
- for(int i = ; i < k; ++ i)
- printf("%d\t", output[i]);
- delete[] output;
- return ;
- }
解法3. 在本地维护好一个大小为k的容器,该容器能够进行自动排序,能够自动排序的容器可以选择STL中的set和multiset,因为它们内部是基于红黑树来实现的,每次插入删除都会进行自动调整。依次读取数组中的一个新元素,与容器中最大的元素做比较,如果小于容器中的最大的元素,则将该最大元素替换,这样循环一遍需要O(n)的复杂度,每次容器进行调整平均是O(logk),这样该算法最终的时间复杂度是O(nlogk)。显而易见,该算法不需要修改原始数据,且适合海量数据处理。
C++代码如下:
- #include "stdafx.h"
- #include <set>
- #include <vector>
- #include <iostream>
- using namespace std;
- int Partition(int data[], int length, int start, int end)
- {
- if(data == NULL || length <= || start < || end >= length)
- throw new std::exception("Invalid Parameters");
- int pivotkey = data[start];// 记录枢轴关键字
- while (start < end)
- {
- while(start<end && data[end]>=pivotkey)
- --end;// 找到从end位置开始向前第一个比枢轴小的元素
- data[start] = data[end];// 将找到的比枢轴小的元素放到前边的空闲位置
- while(start<end && data[start]<=pivotkey)
- ++start;// 找到从start位置开始向后第一个比枢轴大的元素
- data[end] = data[start];// 将找到的比枢轴大的元素放到后边的空闲位置
- }
- data[start] = pivotkey;// 将枢轴放回中间的空闲位置
- return start;
- }
- 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 < || 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);
- }
- }
- }
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- int data[] = {, , , , , , , , , , , , , -};
- int n = ;
- int k = ;
- intSet leastNumbers;
- std::vector<int> dataVec(&data[], &data[]);// 以迭代器初始化,注意第二个参数end是最后一个元素的下一个位置
- GetLeastNumbers(dataVec, leastNumbers, k);
- for(setIterator iter = leastNumbers.begin(); iter != leastNumbers.end(); ++iter)
- printf("%d\t", *iter);
- return ;
- }
如何求出数组中最小(或者最大)的k个数(least k问题)的更多相关文章
- 求一个数组中最小的K个数
方法1:先对数组进行排序,然后遍历前K个数,此时时间复杂度为O(nlgn); 方法2:维护一个容量为K的最大堆(<算法导论>第6章),然后从第K+1个元素开始遍历,和堆中的最大元素比较,如 ...
- 求出数组中所有数字的和&&弹出层效果
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- 黑马基础阶段测试题:定义一个int类型的数组,数组中元素为{5,7,3,9,4}。求出数组中的最小值,并判断最小值是否为偶数,如果是偶数则输出“最小值为偶数”,如果不是偶数则输出“最小值为奇数”。打印如下:
package com.swift; import java.util.Arrays; public class ArrayTest { public static void main(String[ ...
- Ruby求出数组中最小值及其下标
其实很简单 Ruby的Array类自带了min方法可以求出最小值,然后调用Array的index方法传入元素值就可以求出下标 a = [1, 2, 3, 4, 5, 6] theMin = a.min ...
- 【剑指offer】找出数组中出现一次的两个数
2013-09-08 10:50:46 一个整型数组中,除了两个数字之外,其他数字都出现了2次,找出这两个只出现一次的数字,要求时间复杂度是O(N),空间复杂度是O(1). 小结: 任何数与0异或,结 ...
- LeetCode——Single Number II(找出数组中只出现一次的数2)
问题: Given an array of integers, every element appears three times except for one. Find that single o ...
- 《剑指offer》第三_一题(找出数组中重复的数字,可改变数组)
// 面试题3(一):找出数组中重复的数字 // 题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内.数组中某些数字是重复的,但不知道有几个数字重复了, // 也不知道每个数字重复了几次.请 ...
- K:求取数组中最大连续子序列和的四个算法
相关介绍: 求取数组中最大连续子序列和问题,是一个较为"古老"的一个问题.该问题的描述为,给定一个整型数组(当然浮点型也是可以的啦),求取其下标连续的子序列,且其和为该数组的所有 ...
- iOS中求出label中文字的行数和每一行的内容
今天遇到一个需求,需要计算label中文字的行数.想了好久也没想到好的解决方法,就在网上找了下.结果发现一篇文章是讲这个的.这部分代码不但能够求出一个label中文字行数,更厉害的是能够求出每一行的内 ...
随机推荐
- Java SE 8 的流库学习笔记
前言:流提供了一种让我们可以在比集合更高的概念级别上指定计算的数据视图.如: //使用foreach迭代 long count = 0; for (String w : words) { if (w. ...
- JDBC入门(3)--- PrepareStatement
一.PrepareStatement概述 PrepareStatement是Statement接口的子接口: 1.强大之处: 防SQL攻击: 提高代码的可读性: 提高效率; 2.PrepareStat ...
- jvm 内存机制
jvm 的内存包括stack ,Heap,NonHeap,在此重点说明Heap,NonHeap. Heap叫堆内存,它用于存放类实例和数组信息.NonHeap叫非堆内存,用于存放类,方法等可反射的对象 ...
- Vue打包桌面程序
开源的地址:https://github.com/electron/electron-quick-start 一.运行 1. 安装依赖 cnpm install electron --save cnp ...
- Signal & Slot in Qt
Try your best to provide an mechanism to implement what you want. 1. All is generated by QT Framewor ...
- WinSxS文件夹瘦身
WinSxS文件夹瘦身 2014-5-8 18:03:32来源:IT之家作者:阿象责编:阿象 评论:27 刚刚,我们分享了如何用DISM管理工具查看Win8.1 WinSxS文件夹实际大小.对于Win ...
- 如何安装window7 语言包
很多下载的语言包都是.exe文件,双击后生成lp.cab文件. 这时马上把文件复制到另外一个文件夹中,直接拖最快. 这是 window7 sp1 的很多语言包下载地址(企业版和旗舰版通用):http: ...
- visual box 安装 centos7后,无法上网
ifconfig 命令后,只看到个回环网卡: 进入 /etc/sysconfig/network-scripts后,发现有设备 visual box 中设置为“桥接网卡”,也没问题,最后把控制芯片改 ...
- 微信 公众号 小程序 授权 unionid 用户信息 实验总结
-*-*-*-*-*-*-*-*-*--*-*-*-1.小程序通过code获取用户openid的接口,如果用户曾经授权并未过期,或者用户关注过同主体的公众号,会带回unionID,但没有用户头像等信息 ...
- 如何在CentOS 6.7上将PHP 5.3升级到PHP 5.6
如何在CentOS 6.7上将PHP 5.3升级到PHP 5.6 andyz 博客 45评论 Facebook的TwitterGoogle+Reddit 在本文中,我将介绍如何在CentOS ...