题目:

给定一个无序的整型数组arr,找到其中最小的k个数。

方法一:

将数组排序,排序后的数组的前k个数就是最小的k个数。

时间复杂度:O(nlogn)

方法二:

时间复杂度:O(nlogk)

维护一个有k个数的大根堆,这个堆代表目前选出的k个最小的数。在堆的k个元素中堆顶元素是最小的k个数中最大的那个。

接下来要遍历整个数组,遍历的过程中看当前数是否比堆顶元素小。如果是,就把堆顶元素替换成当前数,然后调整堆。如果不是,则不做任何操作,继续遍历下一个数。在遍历完成后,堆中的k个数就是所有数组中最小的k个数。

程序:

  1. public static int[] getMinKNumsByHeap(int[] arr, int k) {
  2. if (k < 1 || k > arr.length) {
  3. return arr;
  4. }
  5. int[] heap = new int[k];
  6. for (int i = 0; i != k; i++) {
  7. heapInsert(heap, arr[i], i);
  8. }
  9. for (int i = k; i < arr.length; i++) {
  10. if (arr[i] < heap[0]) {
  11. heap[0] = arr[i];
  12. heapify(heap, 0, k);
  13. }
  14. }
  15. return heap;
  16. }
  17. private static void heapInsert(int[] heap, int value, int index) {
  18. heap[index] = value;
  19. while (index != 0) {
  20. int parent = (index - 1) / 2;
  21. if (heap[parent] < heap[index]) {
  22. swap(heap, parent, index);
  23. index = parent;
  24. } else {
  25. break;
  26. }
  27. }
  28. }
  29. private static void heapify(int[] heap, int index, int heapSize) {
  30. int left = index * 2 + 1;
  31. int right = index * 2 + 2;
  32. int largest = index;
  33. while (left < heapSize) {
  34. if (heap[left] > heap[index]) {
  35. largest = left;
  36. }
  37. if (right < heapSize && heap[right] > heap[largest]) {
  38. largest = right;
  39. }
  40. if (largest != index) {
  41. swap(heap, largest, index);
  42. } else {
  43. break;
  44. }
  45. index = largest;
  46. left = index * 2 + 1;
  47. right = index * 2 + 2;
  48. }
  49. }
  50. private static void swap(int[] heap, int parent, int index) {
  51. int tmp = heap[index];
  52. heap[index] = heap[parent];
  53. heap[parent] = tmp;
  54. }

方法三:

时间复杂度:O(n)

这里用到了一个经典算法----BFPRT算法。

1973 年, Blum 、 Floyd 、 Pratt 、 Rivest 、 Tarjan 集体出动,合写了一篇题为 “Time bounds for selection” 的论文,给出了一种在数组中选出第 k 大元素的算法,俗称"中位数之中位数算法"。依靠一种精心设计的 pivot 选取方法,该算法从理论上保证了最坏情形下的线性时间复杂度,打败了平均线性、最坏 O(n^2) 复杂度的传统算法。一群大牛把递归算法的复杂度分析玩弄于股掌之间,构造出了一个当之无愧的来自圣经的算法。

算法步骤:

step1:将n个元素每5个一组,分成n/5(上界)组,最后的一个组的元素个数为n%5,有效的组数为n/5。

step2:取出每一组的中位数,最后一个组的不用计算中位数,任意排序方法,这里的数据比较少只有5个,

可以用简单的冒泡排序或是插入排序。

setp3 :  将各组的中位数与数组开头的数据在组的顺序依次交换,这样各个组的中位数都排在了数据的左边。

递归的调用中位数选择算法查找上一步中所有组的中位数的中位数,设为x,偶数个中位数的情况下设定为选取中间小的一个。

setp4:   按照x划分,大于或者等于x的在右边,小于x的在左边,关于setp4数据的划分,中位数放在左边或是右边会有些影响。

后面的代码调试将会看到。

step5:setp4中划分后数据后返回一个下表i,i左边的元素均是小于x,i右边的元素包括i都是大于或是等于x的。

若i==k,返回x;

若i<k,在小于x的元素中递归查找第i小的元素;

若i>k,在大于等于x的元素中递归查找第i-k小的元素。

  1. public static int[] getMinKNumsByBFPRT(int[] arr, int k) {
  2. if (k < 1 || k > arr.length) {
  3. return arr;
  4. }
  5. int minKth = getMinKthByBFPRT(arr, k);
  6. int[] res = new int[k];
  7. int index = 0;
  8. for (int i = 0; i != arr.length; i++) {
  9. if (arr[i] < minKth) {
  10. res[index++] = arr[i];
  11. }
  12. }
  13. for (; index != res.length; index++) {
  14. res[index] = minKth;
  15. }
  16. return res;
  17. }
  18. public static int getMinKthByBFPRT(int[] arr, int K) {
  19. int[] copyArr = copyArray(arr);
  20. return select(copyArr, 0, copyArr.length - 1, K - 1);
  21. }
  22. public static int[] copyArray(int[] arr) {
  23. int[] res = new int[arr.length];
  24. for (int i = 0; i != res.length; i++) {
  25. res[i] = arr[i];
  26. }
  27. return res;
  28. }
  29. public static int select(int[] arr, int begin, int end, int i) {
  30. if (begin == end) {
  31. return arr[begin];
  32. }
  33. int pivot = medianOfMedians(arr, begin, end);
  34. int[] pivotRange = partition(arr, begin, end, pivot);
  35. if (i >= pivotRange[0] && i <= pivotRange[1]) {
  36. return arr[i];
  37. } else if (i < pivotRange[0]) {
  38. return select(arr, begin, pivotRange[0] - 1, i);
  39. } else {
  40. return select(arr, pivotRange[1] + 1, end, i);
  41. }
  42. }
  43. public static int medianOfMedians(int[] arr, int begin, int end) {
  44. int num = end - begin + 1;
  45. int offset = num % 5 == 0 ? 0 : 1;
  46. int[] mArr = new int[num / 5 + offset];
  47. for (int i = 0; i < mArr.length; i++) {
  48. int beginI = begin + i * 5;
  49. int endI = beginI + 4;
  50. mArr[i] = getMedian(arr, beginI, Math.min(end, endI));
  51. }
  52. return select(mArr, 0, mArr.length - 1, mArr.length / 2);
  53. }
  54. public static int[] partition(int[] arr, int begin, int end, int pivotValue) {
  55. int small = begin - 1;
  56. int cur = begin;
  57. int big = end + 1;
  58. while (cur != big) {
  59. if (arr[cur] < pivotValue) {
  60. swap(arr, ++small, cur++);
  61. } else if (arr[cur] > pivotValue) {
  62. swap(arr, cur, --big);
  63. } else {
  64. cur++;
  65. }
  66. }
  67. int[] range = new int[2];
  68. range[0] = small + 1;
  69. range[1] = big - 1;
  70. return range;
  71. }
  72. public static int getMedian(int[] arr, int begin, int end) {
  73. insertionSort(arr, begin, end);
  74. int sum = end + begin;
  75. int mid = (sum / 2) + (sum % 2);
  76. return arr[mid];
  77. }
  78. public static void insertionSort(int[] arr, int begin, int end) {
  79. for (int i = begin + 1; i != end + 1; i++) {
  80. for (int j = i; j != begin; j--) {
  81. if (arr[j - 1] > arr[j]) {
  82. swap(arr, j - 1, j);
  83. } else {
  84. break;
  85. }
  86. }
  87. }
  88. }

[算法]找到无序数组中最小的K个数的更多相关文章

  1. 【算法】数组与矩阵问题——找到无序数组中最小的k个数

    /** * 找到无序数组中最小的k个数 时间复杂度O(Nlogk) * 过程: * 1.一直维护一个有k个数的大根堆,这个堆代表目前选出来的k个最小的数 * 在堆里的k个元素中堆顶的元素是最小的k个数 ...

  2. 《程序员代码面试指南》第八章 数组和矩阵问题 找到无序数组中最小的k 个数

    题目 找到无序数组中最小的k 个数 java代码 package com.lizhouwei.chapter8; /** * @Description: 找到无序数组中最小的k 个数 * @Autho ...

  3. [程序员代码面试指南]数组和矩阵问题-找到无序数组中最小的k个数(堆排序)

    题目链接 https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=13&tqId=11182&t ...

  4. 小米笔试题:无序数组中最小的k个数

    题目描述 链接:https://www.nowcoder.com/questionTerminal/ec2575fb877d41c9a33d9bab2694ba47?source=relative 来 ...

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

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

  6. 窥探算法之美妙——寻找数组中最小的K个数&python中巧用最大堆

    原文发表在我的博客主页,转载请注明出处 前言 不论是小算法或者大系统,堆一直是某种场景下程序员比较亲睐的数据结构,而在python中,由于数据结构的极其灵活性,list,tuple, dict在很多情 ...

  7. 找到数组中最小的k个数

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

  8. 求数组中最小的k个数

    题目:输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. package test; import java.util.Arra ...

  9. [剑指offer]数组中最小的K个数,C++实现

    原创博文,转载请注明出处! http://github.com/wanglei5205 http://cnblogs.com/wanglei5205 # 题目 输入n个整数,找出其中最小的K个数.例如 ...

随机推荐

  1. vim命令行模式

    1. 激活命令行模式 : 进入命令行模式 <Esc>  退出命令行模式 2. 常用命令 :p 打印 (:print) :e 读入文件 (:edit) :w 写入文件 (:write) :t ...

  2. 利用python 掌握机器学习的过程

    转载:http://python.jobbole.com/84326/ 偶然看到的这篇文章,觉得对我挺有引导作用的.特此跟大家分享一下. 为了理解和应用机器学习技术,你需要学习 Python 或者 R ...

  3. iOS iPhoneX/iPhoneXS/iPhoneXR/iPhoneXS Max系列适配

    以前异性屏只有一款iPhoneX,所以在适配的时候直接判断高度是否等于812即可判断是否是iPhoneX #define IS_IPHONE_X (IS_IPHONE && SCREE ...

  4. LCD屏背光驱动调试心得---血的教训

    开发板:明远智睿MY-IMX6-EK140 内核源码:linux-3.14.52 背光驱动IC:MP3202 调光原理:通过开发板的核心板PWM4引脚控制MP3202的EN脚,输出不同的占空比从而达到 ...

  5. 在VS中自动生成NuGet包以及搭建自己的或单位内部的NuGet服务器

    关于NuGet的介绍已经很多,可以参考下面的: NuGet学习笔记(1)--初识NuGet及快速安装使用 http://kb.cnblogs.com/page/143190/ NuGet学习笔记(2) ...

  6. python模块, 包的初识

    Python 模块(Module), 是一个 Python 文件,以 .py 结尾,包含了 Python 对象定义和Python语句. 模块让你能够有逻辑地组织你的 Python 代码段. 把相关的代 ...

  7. springmvc demo

    [说明]今天上午稍稍偏了一下方向,看了看servlet的相关知识,下午做maven+spring+springMVC的整合,晚上成功实现了一个小demo(可以在jsp动态页面上获得通过地址栏输入的参数 ...

  8. 【BZOJ3698】XWW的难题 有上下界的最大流

    [BZOJ3698]XWW的难题 Description XWW是个影响力很大的人,他有很多的追随者.这些追随者都想要加入XWW教成为XWW的教徒.但是这并不容易,需要通过XWW的考核.XWW给你出了 ...

  9. echart 图表自定义样式

    initChart: function (id) { this.charts = echarts.init(document.getElementById(id)) this.charts.setOp ...

  10. android菜鸟学习笔记21----ContentProvider(一)ContentProvider的简单使用

    ContentProvider是Android四大组件之一,它用来封装数据,并通过ContentResolver接口将数据提供给其他应用.只有当需要在多个应用之间共享数据时才会用到ContentPro ...