编程之法:面试和算法心得(寻找最小的k个数)
内容全部来自编程之法:面试和算法心得一书,实现是自己写的使用的是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个数)的更多相关文章
- 编程之法section II: 2.1 求最小的k个数
====数组篇==== 2.1 求最小的k个数: 题目描述:有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 解法一: 思路:快排后输出前k个元素,O(nlogn). writer: zz ...
- 算法练习-寻找最小的k个数
练习问题来源 https://wizardforcel.gitbooks.io/the-art-of-programming-by-july/content/02.01.html 要求 输入n个整数, ...
- 算法练习:寻找最小的k个数
参考July的文章:http://blog.csdn.net/v_JULY_v/article/details/6370650 寻找最小的k个数题目描述:查找最小的k个元素题目:输入n个整数,输出其中 ...
- 03寻找最小的k个数
题目描述:查找最小的k个元素 题目:输入n个整数,输出其中最小的k个. 例如输入1,2,3,4,5,6,7和8这8个数字,则最小的4个数字为1,2,3和4. 1:最简单 ...
- 算法笔记_035:寻找最小的k个数(Java)
目录 1 问题描述 2 解决方案 2.1 全部排序法 2.2 部分排序法 2.3 用堆代替数组法 2.4线性选择算法 1 问题描述 有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 2 ...
- Java实现寻找最小的k个数
1 问题描述 有n个整数,请找出其中最小的k个数,要求时间复杂度尽可能低. 2 解决方案 2.1 全部排序法 先对这n个整数进行快速排序,在依次输出前k个数. package com.liuzhen. ...
- 算法题解:最小的K个数(海量数据Top K问题)
[本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 题目 输入 n ...
- 寻找最小的k个数
1. 能想到的最直接的办法,就是对数组进行排序,最好的排序算法的时间复杂性为O(n*logn),这一个方法请参照各种排序算法. 2. 另外申请一个k空间数组,依次更改里面的最大值,每做一次最多要扫描一 ...
- 寻找最小的k个数(四种方法)
1 使用从大到小的优先队列保存最小的K个数,每次取出K个数之后的其余数和堆顶元素比较,如果比堆顶元素小,则将堆顶元素删除,将该元素插入 void topK(int arr[],int n,int k) ...
随机推荐
- Java中的线程Thread方法之---interrupt()
前几篇都介绍了Thread中的几个方法,相信大家都发现一个相似点,那就是sleep,join,wait这样的阻塞方法都必须捕获一个InterruptedException异常,顾名思义就是一个线程中断 ...
- 线性dp——cf988F
不是很难,dp[i]表示到位置i的最小花费 #include<bits/stdc++.h> using namespace std; #define ll long long #defin ...
- NX二次开发-UFUN获取工程图详细信息UF_DRAW_ask_drawing_info
NX9+VS2012 #include <uf.h> #include <uf_draw.h> #include <uf_part.h> UF_initialize ...
- NX二次开发-遍历当前part所有component,把装配子部件设置成工作部件
NX11+VS2013 #include <uf.h> #include <uf_disp.h> #include <uf_modl.h> #include < ...
- Lvs+keepalived+mysql(主从复制)
1.准备环境 操作系统:centos 6.5 2台机器主机名为node53.node54 Mysql Lvs keepalived node2 Y Y Y node3 Y Y Y ...
- 数据结构C++版-队列
一.概念 分类: 二.补充说明 1.<面向对象的队列设计>课程问答: 首先要明确数据结构和数据存储结构的概念. 数据结构是指数据对象之间的逻辑关系,例如二叉树,队列,栈等,而数据存储结构是 ...
- 终于,Spring 5.0正式发布了!
Spring 5.0都有什么新功能? 1.最低要求支持JDK8+, J2EE 7+. 2.移除了一些包.类及方法. 3.核心功能加强:全面应用jdk8并兼容jdk9等. 4.SpringMVC:支持s ...
- python学习7—函数定义、参数、递归、作用域、匿名函数以及函数式编程
python学习7—函数定义.参数.递归.作用域.匿名函数以及函数式编程 1. 函数定义 def test(x) # discription y = 2 * x return y 返回一个值,则返回原 ...
- Spring MVC入门示例(1)
1.新建一个Java Web项目 2.导入jar包 3.在WEB-INF下面建一个hello.jsp页面. 1 <%@ page language="java" import ...
- Unity实现Android端视频播放
本文只讲Android短的视频播放 实现方式 使用Handheld.PlayFullScreenMovie(),这个函数实现.具体如下: 1.创建StreamingAssets文件夹,此文件夹放入视频 ...