内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是java

题目描述

输入n个整数,输出其中最小的k个。

分析与解法

解法一

要求一个序列中最小的k个数,按照惯有的思维方式,则是先对这个序列从小到大排序,然后输出前面的最小的k个数。

至于选取什么的排序方法,我想你可能会第一时间想到快速排序(我们知道,快速排序平均所费时间为n*logn),然后再遍历序列中前k个元素输出即可。因此,总的时间复杂度:O(n * log n)+O(k)=O(n * log n)。

/*
* 快速排序法 O(N*logN)
* 最简单直接的方法就是快速排序+返回前k个数字
*/
public static int[] solution1(int[] arr, int k)
{
Arrays.sort(arr);
int[] result = new int[k];
for (int i = 0; i < k; i++)
{
result[i] = arr[i];
}
return result;
}

解法二

咱们再进一步想想,题目没有要求最小的k个数有序,也没要求最后n-k个数有序。既然如此,就没有必要对所有元素进行排序。这时,咱们想到了用选择或交换排序,即:

1、遍历n个数,把最先遍历到的k个数存入到大小为k的数组中,假设它们即是最小的k个数;2、对这k个数,利用选择或交换排序找到这k个元素中的最大值kmax(找最大值需要遍历这k个数,时间复杂度为O(k));3、继续遍历剩余n-k个数。假设每一次遍历到的新的元素的值为x,把x与kmax比较:如果x < kmax ,用x替换kmax,并回到第二步重新找出k个元素的数组中最大元素kmax‘;如果x >= kmax,则继续遍历不更新数组。

每次遍历,更新或不更新数组的所用的时间为O(k)或O(0)。故整趟下来,时间复杂度为n*O(k)=O(n*k)。

/*
* 1、遍历n个数,把最先遍历到的k个数存入到大小为k的数组中,假设它们即是最小的k个数;
* 2、对这k个数,利用选择或交换排序找到这k个元素中的最大值kmax(找最大值需要遍历这k个数,时间复杂度为O(k));
* 3、继续遍历剩余n-k个数。假设每一次遍历到的新的元素的值为x,把x与kmax比较:如果x < kmax ,用x替换kmax,并回到第二步重新找出k个元素的数组中最大元素kmax‘;如果x >= kmax,则继续遍历不更新数组。
*/
private static int[] solution2(int[] arr, int k) {
chooseSort(arr, k);
int[] res = new int[k];
for (int i = 0; i < k; i++)
res[i] = arr[i];
return res;
} private static void chooseSort(int[] arr, int k) {
for (int i = 0; i < k; i++)
{
int index = i;
for (int j = i; j < arr.length; j++)
{
if (arr[index] > arr[j])
{
index = j;
}
}
int temp = arr[index];
arr[index] = arr[i];
arr[i] = temp;
}
}

解法三

更好的办法是维护容量为k的最大堆,原理跟解法二的方法相似:

  • 1、用容量为k的最大堆存储最先遍历到的k个数,同样假设它们即是最小的k个数;
  • 2、堆中元素是有序的,令k1<k2<...<kmax(kmax设为最大堆中的最大元素)
  • 3、遍历剩余n-k个数。假设每一次遍历到的新的元素的值为x,把x与堆顶元素kmax比较:如果x < kmax,用x替换kmax,然后更新堆(用时logk);否则不更新堆。

这样下来,总的时间复杂度:O(k+(n-k)*logk)=O(n*logk)。此方法得益于堆中进行查找和更新的时间复杂度均为:O(logk)(若使用解法二:在数组中找出最大元素,时间复杂度:O(k))。

解法四

在《数据结构与算法分析--c语言描述》一书,第7章第7.7.6节中,阐述了一种在平均情况下,时间复杂度为O(N)的快速选择算法。如下述文字:

  • 选取S中一个元素作为枢纽元v,将集合S-{v}分割成S1和S2,就像快速排序那样如果k <= |S1|,那么第k个最小元素必然在S1中。在这种情况下,返回QuickSelect(S1, k)。如果k = 1 + |S1|,那么枢纽元素就是第k个最小元素,即找到,直接返回它。否则,这第k个最小元素就在S2中,即S2中的第(k - |S1| - 1)个最小元素,我们递归调用并返回QuickSelect(S2, k - |S1| - 1)。

此算法的平均运行时间为O(n)。

/*
* 以k为分界的分治排序思想 O(N*logK)
*/
private static int[] solution3(int[] arr, int k) {
int[] res = new int[k];
for (int i = 0; i < k; i++)
res[i] = -1;
keySort(arr, 0, arr.length - 1, k, res);
return res;
} private static void keySort(int[] arr, int start, int end, int k, int[] res) {
if (start >= end || k <= 0)
return;
int index = start;
int i = start, j = end + 1;
while (true) {
while (arr[index] > arr[++i]) if (i == end) break;
while (arr[index] < arr[--j]) if (j == start) break;
if (i >= j)
break;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
int temp = arr[index];
arr[index] = arr[j];
arr[j] = temp;
index = j;
if (index - start + 1 > k) keySort(arr, start, index - 1, k, res);
else {
for (i = 0, j = 0; j < index - start + 1; i++)
if (res[i] == -1) res[i] = arr[start + (j++)];
if (index - start + 1 == k) return;
else keySort(arr, index + 1, end, k - index + start - 1, res);
} }

参考

java 最快获取最小前K个数

编程之法:面试和算法心得(寻找最小的k个数)的更多相关文章

  1. 编程之法section II: 2.1 求最小的k个数

    ====数组篇==== 2.1 求最小的k个数: 题目描述:有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 解法一: 思路:快排后输出前k个元素,O(nlogn). writer: zz ...

  2. 算法练习-寻找最小的k个数

    练习问题来源 https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/02.01.html 要求 输入n个整数, ...

  3. 算法练习:寻找最小的k个数

    参考July的文章:http://blog.csdn.net/v_JULY_v/article/details/6370650 寻找最小的k个数题目描述:查找最小的k个元素题目:输入n个整数,输出其中 ...

  4. 03寻找最小的k个数

    题目描述:查找最小的k个元素         题目:输入n个整数,输出其中最小的k个.         例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 1:最简单 ...

  5. 算法笔记_035:寻找最小的k个数(Java)

    目录 1 问题描述 2 解决方案 2.1 全部排序法 2.2 部分排序法 2.3 用堆代替数组法 2.4线性选择算法   1 问题描述 有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 2 ...

  6. Java实现寻找最小的k个数

    1 问题描述 有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 2 解决方案 2.1 全部排序法 先对这n个整数进行快速排序,在依次输出前k个数. package com.liuzhen. ...

  7. 算法题解:最小的K个数(海量数据Top K问题)

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 题目 输入 n ...

  8. 寻找最小的k个数

    1. 能想到的最直接的办法,就是对数组进行排序,最好的排序算法的时间复杂性为O(n*logn),这一个方法请参照各种排序算法. 2. 另外申请一个k空间数组,依次更改里面的最大值,每做一次最多要扫描一 ...

  9. 寻找最小的k个数(四种方法)

    1 使用从大到小的优先队列保存最小的K个数,每次取出K个数之后的其余数和堆顶元素比较,如果比堆顶元素小,则将堆顶元素删除,将该元素插入 void topK(int arr[],int n,int k) ...

随机推荐

  1. thinkphp自动创建目录

    自动创建目录 在第一次访问应用入口文件的时候,会显示如图所示的默认的欢迎页面,并自动生成了一个默认的应用模块Home. 接下来再看原来空的Application目录下面,已经自动生成了公共模块Comm ...

  2. 26 函数形参值回传问题——C++解决多个return的一般方法

    0 引言 在使用数组和vector作为函数的参数进行参数传递并希望得到值的回传时,由于不知道怎么写数组函数形参的引用形式,一直采用vector的引用形式.但是,刚刚测试了一下,发现数组作为参数本身就是 ...

  3. CF1265B Beautiful Numbers

    题意 给一个长度为\(n\)的排列\(P\),求对于\(1\) 到 \(n\)中的每个数\(m\),是否能找到一段长度为\(m\)的区间使得区间内的数是一个\(1\)到\(m\)的排列. 输出一个\( ...

  4. Openstack-L 路由注入方式

    目录 目录 前言 从 Commands 到 Action 操作函数 前言 Openstack 新旧版本提供了不同的路由注入方式,也就是 Route Module 的代码方式不同,就二次开发而言用那一种 ...

  5. How to Add Swap on CentOS

    About Linux Swapping Linux RAM is composed of chunks of memory called pages. To free up pages of RAM ...

  6. 导出lab动态命名文件夹文件名

    set tmp=%date:~0,10%echo %tmp:/=-%mkdir %tmp:/=-%cd %tmp:/=-%exp meet/meet@orcl_11 file=%tmp:/=-%.dm ...

  7. jQuery 引入多个库文件冲突

    index.html <head><meta http-equiv="Content-Type" content="text/html; charset ...

  8. Codeforces 166B - Polygon (判断凸包位置关系)

    Codeforces Round #113 (Div. 2) 题目链接:Polygons You've got another geometrical task. You are given two ...

  9. Opencv稍微高级点的鼠标事件-OpenCV步步精深

    今天我们要来点稍微高级的东西.在我们按下鼠标时可以画矩形,而我们按下键盘m键时,切换到画圆的模式,再按下m键,回到画矩形模式. 一起来写下代码,首先当然还是调用库 import cv2 import ...

  10. Game of Taking Stones && POJ1259 /// 最大空凸包 几何+DP

    题目大意: 给定n个点 求出这n个点中最大空凸包的面积 只放个模板 一份模板过两题(滑稽 这个讲解够详细了 https://blog.csdn.net/nyroro/article/details/4 ...