输入n个整数,如何求出其中最小的k个数?

解法1. 当然最直观的思路是将数组排序,然后就可以找出其中最小的k个数了,时间复杂度以快速排序为例,是O(nlogn);

解法2. 借助划分(Partition)的思路,一次划分可以把枢轴使得枢轴左边的元素都比枢轴小,枢轴右边的元素都比枢轴大(可以参考快速排序及STL中的sort算法)。那么可以基于数组的第k个数字来调整,使得比第k个数字小的数字都位于数组的左边,使得比第k个数字大的数字都位于数组的右边。那么调整完毕后,数组中左边的k个数字就是最小的k个数字(这k个数字不一定是排序的)。该解法时间复杂度最低,是O(n),但需要修改输入的数组,如果要求不能修改输入的数组,那就行不通了。另外该算法也不大适合海量数据处理,因为没办法一次读入内存,只能一次读取一些。

  C++代码如下:

  1. #include "stdafx.h"
  2. #include <set>
  3. #include <vector>
  4. #include <iostream>
  5.  
  6. using namespace std;
  7. int Partition(int data[], int length, int start, int end)
  8. {
  9. if(data == NULL || length <= || start < || end >= length)
  10. throw new std::exception("Invalid Parameters");
  11.  
  12. int pivotkey = data[start];// 记录枢轴关键字
  13. while (start < end)
  14. {
  15. while(start<end && data[end]>=pivotkey)
  16. --end;// 找到从end位置开始向前第一个比枢轴小的元素
  17. data[start] = data[end];// 将找到的比枢轴小的元素放到前边的空闲位置
  18. while(start<end && data[start]<=pivotkey)
  19. ++start;// 找到从start位置开始向后第一个比枢轴大的元素
  20. data[end] = data[start];// 将找到的比枢轴大的元素放到后边的空闲位置
  21. }
  22. data[start] = pivotkey;// 将枢轴放回中间的空闲位置
  23.  
  24. return start;
  25. }
  26.  
  27. void GetLeastNumbers(int* input, int n, int* output, int k)
  28. {
  29. if(input == NULL || output == NULL || k > n || n <= || k <= )
  30. return;
  31.  
  32. int start = ;
  33. int end = n - ;
  34. int index = Partition(input, n, start, end);
  35. while(index != k - )
  36. {
  37. if(index > k - )
  38. {
  39. end = index - ;
  40. index = Partition(input, n, start, end);
  41. }
  42. else
  43. {
  44. start = index + ;
  45. index = Partition(input, n, start, end);
  46. }
  47. }
  48.  
  49. for(int i = ; i < k; ++i)
  50. output[i] = input[i];
  51. }
  52.  
  53. int _tmain(int argc, _TCHAR* argv[])
  54. {
  55. int data[] = {, , , , , , , , , , , , , -};
  56. int n = ;
  57. int k = ;
  58. int* output = new int[k];
  59. GetLeastNumbers(data, n, output, k);
  60.  
  61. for (int i=; i<n; i++)
  62. {
  63. printf("%d\t", data[i]);
  64. }
  65. printf("\r\n");
  66. for(int i = ; i < k; ++ i)
  67. printf("%d\t", output[i]);
  68. delete[] output;
  69. return ;
  70. }

解法3. 在本地维护好一个大小为k的容器,该容器能够进行自动排序,能够自动排序的容器可以选择STL中的set和multiset,因为它们内部是基于红黑树来实现的,每次插入删除都会进行自动调整。依次读取数组中的一个新元素,与容器中最大的元素做比较,如果小于容器中的最大的元素,则将该最大元素替换,这样循环一遍需要O(n)的复杂度,每次容器进行调整平均是O(logk),这样该算法最终的时间复杂度是O(nlogk)。显而易见,该算法不需要修改原始数据,且适合海量数据处理。

  C++代码如下:

  1. #include "stdafx.h"
  2. #include <set>
  3. #include <vector>
  4. #include <iostream>
  5.  
  6. using namespace std;
  7. int Partition(int data[], int length, int start, int end)
  8. {
  9. if(data == NULL || length <= || start < || end >= length)
  10. throw new std::exception("Invalid Parameters");
  11.  
  12. int pivotkey = data[start];// 记录枢轴关键字
  13. while (start < end)
  14. {
  15. while(start<end && data[end]>=pivotkey)
  16. --end;// 找到从end位置开始向前第一个比枢轴小的元素
  17. data[start] = data[end];// 将找到的比枢轴小的元素放到前边的空闲位置
  18. while(start<end && data[start]<=pivotkey)
  19. ++start;// 找到从start位置开始向后第一个比枢轴大的元素
  20. data[end] = data[start];// 将找到的比枢轴大的元素放到后边的空闲位置
  21. }
  22. data[start] = pivotkey;// 将枢轴放回中间的空闲位置
  23.  
  24. return start;
  25. }
  26.  
  27. typedef multiset<int, greater<int> > intSet;
  28. typedef multiset<int, greater<int> >::iterator setIterator;
  29.  
  30. void GetLeastNumbers(const vector<int>& data, intSet& leastNumbers, int k)
  31. {
  32. leastNumbers.clear();
  33.  
  34. if(k < || data.size() < k)
  35. return;
  36.  
  37. vector<int>::const_iterator iter = data.begin();
  38. for(; iter != data.end(); ++ iter)
  39. {
  40. if((leastNumbers.size()) < k)
  41. leastNumbers.insert(*iter);
  42.  
  43. else
  44. {
  45. setIterator iterGreatest = leastNumbers.begin();
  46.  
  47. if(*iter < *(leastNumbers.begin()))
  48. {
  49. leastNumbers.erase(iterGreatest);
  50. leastNumbers.insert(*iter);
  51. }
  52. }
  53. }
  54. }
  55.  
  56. int _tmain(int argc, _TCHAR* argv[])
  57. {
  58. int data[] = {, , , , , , , , , , , , , -};
  59. int n = ;
  60. int k = ;
  61. intSet leastNumbers;
  62.  
  63. std::vector<int> dataVec(&data[], &data[]);// 以迭代器初始化,注意第二个参数end是最后一个元素的下一个位置
  64. GetLeastNumbers(dataVec, leastNumbers, k);
  65.  
  66. for(setIterator iter = leastNumbers.begin(); iter != leastNumbers.end(); ++iter)
  67. printf("%d\t", *iter);
  68.  
  69. return ;
  70. }

如何求出数组中最小(或者最大)的k个数(least k问题)的更多相关文章

  1. 求一个数组中最小的K个数

    方法1:先对数组进行排序,然后遍历前K个数,此时时间复杂度为O(nlgn); 方法2:维护一个容量为K的最大堆(<算法导论>第6章),然后从第K+1个元素开始遍历,和堆中的最大元素比较,如 ...

  2. 求出数组中所有数字的和&&弹出层效果

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  3. 黑马基础阶段测试题:定义一个int类型的数组,数组中元素为{5,7,3,9,4}。求出数组中的最小值,并判断最小值是否为偶数,如果是偶数则输出“最小值为偶数”,如果不是偶数则输出“最小值为奇数”。打印如下:

    package com.swift; import java.util.Arrays; public class ArrayTest { public static void main(String[ ...

  4. Ruby求出数组中最小值及其下标

    其实很简单 Ruby的Array类自带了min方法可以求出最小值,然后调用Array的index方法传入元素值就可以求出下标 a = [1, 2, 3, 4, 5, 6] theMin = a.min ...

  5. 【剑指offer】找出数组中出现一次的两个数

    2013-09-08 10:50:46 一个整型数组中,除了两个数字之外,其他数字都出现了2次,找出这两个只出现一次的数字,要求时间复杂度是O(N),空间复杂度是O(1). 小结: 任何数与0异或,结 ...

  6. LeetCode——Single Number II(找出数组中只出现一次的数2)

    问题: Given an array of integers, every element appears three times except for one. Find that single o ...

  7. 《剑指offer》第三_一题(找出数组中重复的数字,可改变数组)

    // 面试题3(一):找出数组中重复的数字 // 题目:在一个长度为n的数组里的所有数字都在0到n-1的范围内.数组中某些数字是重复的,但不知道有几个数字重复了, // 也不知道每个数字重复了几次.请 ...

  8. K:求取数组中最大连续子序列和的四个算法

    相关介绍:  求取数组中最大连续子序列和问题,是一个较为"古老"的一个问题.该问题的描述为,给定一个整型数组(当然浮点型也是可以的啦),求取其下标连续的子序列,且其和为该数组的所有 ...

  9. iOS中求出label中文字的行数和每一行的内容

    今天遇到一个需求,需要计算label中文字的行数.想了好久也没想到好的解决方法,就在网上找了下.结果发现一篇文章是讲这个的.这部分代码不但能够求出一个label中文字行数,更厉害的是能够求出每一行的内 ...

随机推荐

  1. IDEA 如何加上 tomcat

    前言: idea 上已经有一个 tomcat 了,因为这个 tomcat 为 32 的,需要加一个 64 为的 tomcat . 第一步: 第二步: 第三步: 点击 OK 就好. 结果:

  2. 为什么推荐用ui-router替代ngRoute

    初学angularjs,第一个实例是官网的phoneCat,里面路由用的是ngRoute,后来看到别的用ui-router,觉得好奇,ui-route是什么呢?百度一些,得到如下解释: ui-rout ...

  3. Android学习笔记(1)----播放音乐文件

    原文地址:http://www.cnblogs.com/wynet/p/5526905.html 这里介绍两种播放资源文件的方法: 第一种. assets类资源放在工程根目录的assets子目录下,它 ...

  4. 上海ctf2017 pwn100 && pwn200

    前言 分析的 idb 文件在这: https://gitee.com/hac425/blog_data/tree/master/shanghaictf pwn100 程序是一个经典的 堆管理程序,基本 ...

  5. Android根据URL下载文件保存到SD卡

    //下载具体操作 private void download() { try { URL url = new URL(downloadUrl); //打开连接 URLConnection conn = ...

  6. /etc/vsftpd.conf详解

    #################匿名权限控制############### anonymous_enable=YES  #是否启用匿名用户no_anon_password=YES #匿名用户logi ...

  7. [Swift] 使用Playground

    使用Playground 1. 新建Playground 2. 写最简单的代码

  8. python传递参数给shell

    #格式化字符 print "hello, %s" % ('mm') #传递参数 n="192.168.200.2" os.popen('ping %s -c 2 ...

  9. Druid学习---配置_DruidDataSource参考配置

    [更多参考]https://www.cnblogs.com/niejunlei/p/5977895.html 配置_DruidDataSource参考配置 以下是一个参考的连接池配置: <bea ...

  10. LVS跨网段DR模式并使用ldirectord实现RS高可用性

    DR模型的工作过程: Client向VIP发起请求,请求被路由器接收到,转发给不同网段的Director的VIP,Director再通过私有网络转给RS服务器,RS服务器处理请求并通过自身配置的VIP ...