[算法]找到无序数组中最小的K个数
题目:
给定一个无序的整型数组arr,找到其中最小的k个数。
方法一:
将数组排序,排序后的数组的前k个数就是最小的k个数。
时间复杂度:O(nlogn)
方法二:
时间复杂度:O(nlogk)
维护一个有k个数的大根堆,这个堆代表目前选出的k个最小的数。在堆的k个元素中堆顶元素是最小的k个数中最大的那个。
接下来要遍历整个数组,遍历的过程中看当前数是否比堆顶元素小。如果是,就把堆顶元素替换成当前数,然后调整堆。如果不是,则不做任何操作,继续遍历下一个数。在遍历完成后,堆中的k个数就是所有数组中最小的k个数。
程序:
- public static int[] getMinKNumsByHeap(int[] arr, int k) {
- if (k < 1 || k > arr.length) {
- return arr;
- }
- int[] heap = new int[k];
- for (int i = 0; i != k; i++) {
- heapInsert(heap, arr[i], i);
- }
- for (int i = k; i < arr.length; i++) {
- if (arr[i] < heap[0]) {
- heap[0] = arr[i];
- heapify(heap, 0, k);
- }
- }
- return heap;
- }
- private static void heapInsert(int[] heap, int value, int index) {
- heap[index] = value;
- while (index != 0) {
- int parent = (index - 1) / 2;
- if (heap[parent] < heap[index]) {
- swap(heap, parent, index);
- index = parent;
- } else {
- break;
- }
- }
- }
- private static void heapify(int[] heap, int index, int heapSize) {
- int left = index * 2 + 1;
- int right = index * 2 + 2;
- int largest = index;
- while (left < heapSize) {
- if (heap[left] > heap[index]) {
- largest = left;
- }
- if (right < heapSize && heap[right] > heap[largest]) {
- largest = right;
- }
- if (largest != index) {
- swap(heap, largest, index);
- } else {
- break;
- }
- index = largest;
- left = index * 2 + 1;
- right = index * 2 + 2;
- }
- }
- private static void swap(int[] heap, int parent, int index) {
- int tmp = heap[index];
- heap[index] = heap[parent];
- heap[parent] = tmp;
- }
方法三:
时间复杂度: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小的元素。
- public static int[] getMinKNumsByBFPRT(int[] arr, int k) {
- if (k < 1 || k > arr.length) {
- return arr;
- }
- int minKth = getMinKthByBFPRT(arr, k);
- int[] res = new int[k];
- int index = 0;
- for (int i = 0; i != arr.length; i++) {
- if (arr[i] < minKth) {
- res[index++] = arr[i];
- }
- }
- for (; index != res.length; index++) {
- res[index] = minKth;
- }
- return res;
- }
- public static int getMinKthByBFPRT(int[] arr, int K) {
- int[] copyArr = copyArray(arr);
- return select(copyArr, 0, copyArr.length - 1, K - 1);
- }
- public static int[] copyArray(int[] arr) {
- int[] res = new int[arr.length];
- for (int i = 0; i != res.length; i++) {
- res[i] = arr[i];
- }
- return res;
- }
- public static int select(int[] arr, int begin, int end, int i) {
- if (begin == end) {
- return arr[begin];
- }
- int pivot = medianOfMedians(arr, begin, end);
- int[] pivotRange = partition(arr, begin, end, pivot);
- if (i >= pivotRange[0] && i <= pivotRange[1]) {
- return arr[i];
- } else if (i < pivotRange[0]) {
- return select(arr, begin, pivotRange[0] - 1, i);
- } else {
- return select(arr, pivotRange[1] + 1, end, i);
- }
- }
- public static int medianOfMedians(int[] arr, int begin, int end) {
- int num = end - begin + 1;
- int offset = num % 5 == 0 ? 0 : 1;
- int[] mArr = new int[num / 5 + offset];
- for (int i = 0; i < mArr.length; i++) {
- int beginI = begin + i * 5;
- int endI = beginI + 4;
- mArr[i] = getMedian(arr, beginI, Math.min(end, endI));
- }
- return select(mArr, 0, mArr.length - 1, mArr.length / 2);
- }
- public static int[] partition(int[] arr, int begin, int end, int pivotValue) {
- int small = begin - 1;
- int cur = begin;
- int big = end + 1;
- while (cur != big) {
- if (arr[cur] < pivotValue) {
- swap(arr, ++small, cur++);
- } else if (arr[cur] > pivotValue) {
- swap(arr, cur, --big);
- } else {
- cur++;
- }
- }
- int[] range = new int[2];
- range[0] = small + 1;
- range[1] = big - 1;
- return range;
- }
- public static int getMedian(int[] arr, int begin, int end) {
- insertionSort(arr, begin, end);
- int sum = end + begin;
- int mid = (sum / 2) + (sum % 2);
- return arr[mid];
- }
- public static void insertionSort(int[] arr, int begin, int end) {
- for (int i = begin + 1; i != end + 1; i++) {
- for (int j = i; j != begin; j--) {
- if (arr[j - 1] > arr[j]) {
- swap(arr, j - 1, j);
- } else {
- break;
- }
- }
- }
- }
[算法]找到无序数组中最小的K个数的更多相关文章
- 【算法】数组与矩阵问题——找到无序数组中最小的k个数
/** * 找到无序数组中最小的k个数 时间复杂度O(Nlogk) * 过程: * 1.一直维护一个有k个数的大根堆,这个堆代表目前选出来的k个最小的数 * 在堆里的k个元素中堆顶的元素是最小的k个数 ...
- 《程序员代码面试指南》第八章 数组和矩阵问题 找到无序数组中最小的k 个数
题目 找到无序数组中最小的k 个数 java代码 package com.lizhouwei.chapter8; /** * @Description: 找到无序数组中最小的k 个数 * @Autho ...
- [程序员代码面试指南]数组和矩阵问题-找到无序数组中最小的k个数(堆排序)
题目链接 https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=13&tqId=11182&t ...
- 小米笔试题:无序数组中最小的k个数
题目描述 链接:https://www.nowcoder.com/questionTerminal/ec2575fb877d41c9a33d9bab2694ba47?source=relative 来 ...
- 求一个数组中最小的K个数
方法1:先对数组进行排序,然后遍历前K个数,此时时间复杂度为O(nlgn); 方法2:维护一个容量为K的最大堆(<算法导论>第6章),然后从第K+1个元素开始遍历,和堆中的最大元素比较,如 ...
- 窥探算法之美妙——寻找数组中最小的K个数&python中巧用最大堆
原文发表在我的博客主页,转载请注明出处 前言 不论是小算法或者大系统,堆一直是某种场景下程序员比较亲睐的数据结构,而在python中,由于数据结构的极其灵活性,list,tuple, dict在很多情 ...
- 找到数组中最小的k个数
/*输入整数数组 arr ,找出其中最小的 k 个数.例如,输入4.5.1.6.2.7.3.8这8个数字, 则最小的4个数字是1.2.3.4. 示例 1: 输入:arr = [3,2,1], k = ...
- 求数组中最小的k个数
题目:输入n个整数,找出其中最小的K个数.例如输入4,5,1,6,2,7,3,8这8个数字,则最小的4个数字是1,2,3,4,. package test; import java.util.Arra ...
- [剑指offer]数组中最小的K个数,C++实现
原创博文,转载请注明出处! http://github.com/wanglei5205 http://cnblogs.com/wanglei5205 # 题目 输入n个整数,找出其中最小的K个数.例如 ...
随机推荐
- SELECT * INTO xx FROM x0
insert into a select * from b:--向存在表中插入数据,如果不存在表a报错. select * into a from b:--创建新表的同时插入数据,如果表a存在,报错. ...
- hdu 1540/POJ 2892 Tunnel Warfare 【线段树区间合并】
Tunnel Warfare Time Limit: 4000/2000 MS ...
- android 语音识别
Android中主要通过RecognizerIntent来实现语音识别,事实上代码比較简单.可是假设找不到设置,就会抛出异常ActivityNotFoundException.所以我们须要捕捉这个异常 ...
- 解密和解压浏览器上加密的js文件
F12 -> 进入Sources -> 找到任意一个加密的js文件,如图 点击最下方的 {} 即可解压
- idea设置自定义快捷键
在说明之前,先引入一个非常好的 Intellij Idea中文教程:intelliJ Idea中文教程 一创建模板类 二使用方法备注 在IntellijIdea中我并没有找到直接对某个方法进行注释的模 ...
- 安装wamp后 异常Exception Exception in module wampmanager.exe at 000F15A0
系统环境:Windows 2008 R2 64bit 安装环境:wampserver2.4-x64 按照正常windows安装程序,完成WAMP Server程序安装,安装完成启动WAMP Serve ...
- Objective-C 的动态提示和技巧
过去的几年中涌现了大量的Objective-C开发者.有些是从动态语言转过来的,比如Ruby或Python,有些是从强类型语言转过来的,如Java或C#,当然也有直接以Objective-C作为入门语 ...
- 正负小数点后两位浮点数--jquery
背景:项目中需要做个对两位小数点的正负浮点数的处理, 要求:非数字或者.字符自动清除,并对.12自动修补.前的0 原理:在输入框中加入两个事件,keyup与blur,keyup处理字符串中非要求的字符 ...
- 11 Memcached 缓存雪崩现象
一:Memcached 缓存雪崩现象(1)造成原因:一般是由某个节点失效,导致其他的节点的缓存命中率下降,缓存中缺失的数据查询,短时间内造成的数据库服务器奔溃.重启DB,短期内又被压垮,但缓存的数据增 ...
- Struts 2 框架特性
POJO形式和POJO动作 - 已经摆脱了Struts2的动作表单的Struts框架的一个组成部分.Struts2可以使用任何的POJO接收的形式输入.同样的,你现在可以看到任何POJO的Action ...