题目:n个数中,求最小的前k个数。

这道题在各个地方都看到过,在国内出现的频率也非常高。

面完阿里回来听说这道题又被考了,所以还是决定回来写一写,对于这种高频题。。。顺便再吐槽一下阿里的面试,我竟然一道题都不用做,只是纯粹地过简历。。。导致我都不知道我究竟错在哪里。

解法:

1. brute force。 O(k*n)复杂度;

2. sort。O(k+n*lgn)复杂度;

3. 最大堆。每次替代的是大小为k的最大堆的最大值。O(k+(n-k)lgk)复杂度。

 int findKthByHeap(int arr[], int n, int k) {
make_heap(arr, arr + k); for (int i = k; i < n; ++i) {
if (arr[i] < arr[]) {
pop_heap(arr, arr + k); // pop_heap()用于弹出堆中的第一个元素,并把它放到区间的最后一个位置,然后重新将前面的元素构建成一个堆。
arr[k - ] = arr[i]; //替换最后一个数
push_heap(arr, arr + k); //pop_heap()用于将指定区间的最后一个元素加入堆中并使整个区间成为一个新的堆。注意前提是最后一个元素除外的所有元素已经构成一个堆。
}
} return arr[];
}

4. 最小堆。和sort类似,只是建堆后只求前k次。O(n+k*lgn)复杂度。在网上看到一个优化,就是pop出第k小的数(堆顶)的时候,最多只需要调整k-1层(不需要调到堆底)。所以可以优化到O(n+k^2)。当然这个建堆需要O(n)的空间复杂度,所以还是弱一点。

 int findKthByHeap(int arr[], int n, int k) {
make_heap(arr, arr + n, greater<int>());
for (int i = ; i < k - ; ++i) {
pop_heap(arr, arr + n, greater<int>());
n--;
}
return arr[];
}

5. 类quick sort。求第k小的数。pivot用的是“五分化中项的中项”。因为每次划分之后,只需要考虑其中一部分的数,证明过程类似于堆排序建堆用了O(n)的开销证明。开销也在O(n)。

关于partition,要用的是双向划分, 避免的是所有数字都相等的情况。

双向划分也有两种方式,最后一种最为简洁,关键在于arr[l]这个元素因为先保存下来了,所以替换它是安全的,所以我们是先找arr[r],然后将它保存到arr[l];然后再找arr[l],将它保存到arr[r]。循环退出时,arr[l]已经保存到arr[r]的位置了,所以循环不变式是arr[l]仍然可以安全地被替代。

 int partition2(int arr[], int n) {
int l = ;
for (int i = ; i < n; ++i) {
if (arr[i] < arr[]) {
swap(arr[++l], arr[i]);
}
}
swap(arr[], arr[l]);
return l;
} int partition(int arr[], int n) {
int l = , r = n;
while (true) {
while (++l < n && arr[l] < arr[]);
while (arr[--r] > arr[]);
if (l >= r) break;
swap(arr[l], arr[r]);
}
swap(arr[], arr[r]);
return r;
} int partition3(int arr[], int n) {
int l = , r = n - ;
int p = arr[];
while (l < r) {
while (r > l && arr[r] >= p) r--;
arr[l] = arr[r];
while (r > l && arr[l] <= p) l++;
arr[r] = arr[l];
}
arr[l] = p;
return l;
} int quickSelect(int arr[], int n, int k) {
int p = partition3(arr, n);
if (p == k - ) return arr[p];
else if (p < k - ) {
return quickSelect(arr + p + , n - p - , k - p - );
} else {
return quickSelect(arr, p, k);
}
}

“五分化中项的中项”划分法:

  • 将输入数组的N个元素划分为[n/5]组,最后一个组剩下的n mod5组成;
  • 寻找每一组的中位数:首先对每组的元素进行插入排序,排序后选出一些中位数;这样可以确保,对于这一些中位数,大于它们的数的个数约等于小于它们的数的个数;
  • 对找出的[n/5]个中位数,继续递归找到其中位数,作为最终的pivot;
  • 基于pivot进行partition划分;

找到n中最小的k个数的更多相关文章

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

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

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

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

  3. [算法]找到无序数组中最小的K个数

    题目: 给定一个无序的整型数组arr,找到其中最小的k个数. 方法一: 将数组排序,排序后的数组的前k个数就是最小的k个数. 时间复杂度:O(nlogn) 方法二: 时间复杂度:O(nlogk) 维护 ...

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

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

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

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

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

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

  7. 求给定数据中最小的K个数

    public class MinHeap { /* * * Top K个问题,求给定数据中最小的K个数 * * 最小堆解决:堆顶元素为堆中最大元素 * * * */ private int MAX_D ...

  8. Java找N个数中最小的K个数,PriorityQueue和Arrays.sort()两种实现方法

    最近看到了 java.util.PriorityQueue.刚看到还没什么感觉,今天突然发现他可以用来找N个数中最小的K个数. 假设有如下 10 个整数. 5 2 0 1 4 8 6 9 7 3 怎么 ...

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

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

随机推荐

  1. Java面向对象的封装

    封装是Java面向对象的三大特性之一,通常我们是通过包管理机制同时对类进行封装,隐藏其内部实现细节,通常开发中不允许直接操作类中的成员属性,所以属性一般设置为私有权限private,类中一般会给出一些 ...

  2. Java for LeetCode 141 Linked List Cycle

    Given a linked list, determine if it has a cycle in it. Follow up: Can you solve it without using ex ...

  3. HDU 1848 Fibonacci again and again (斐波那契博弈SG函数)

    Fibonacci again and again Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & ...

  4. 解决ubuntu14.04下Qt 5.3.1下的QtCreator fcitx,ibus不能输入中文

    http://my.oschina.net/u/219482/blog/341452 感谢作者 ubuntu 14.04从Qt官网下载的最新版qt,安装过程很顺利,但却发现没办法输入中文(我用的是 f ...

  5. struts2的s:iterator 标签 详解

    s:iterator 标签有3个属性:value:被迭代的集合id   :指定集合里面的元素的idstatus 迭代元素的索引1:jsp页面定义元素写法 数组或list <s:iterator ...

  6. [转]Android的Handler总结

    一.Handler的定义:          主要接受子线程发送的数据, 并用此数据配合主线程更新UI.          解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线 ...

  7. HDU 4341 分组背包

    B - Gold miner Time Limit:2000MS      Memory Limit:32768KB     Description Homelesser likes playing ...

  8. loj 1011(状态压缩+记忆化搜索)

    题目链接:http://acm.hust.edu.cn/vjudge/problem/viewProblem.action?id=25837 思路:状态压缩+记忆化搜索. #include<io ...

  9. ScrollView与ListView的冲突

    众所周知ListView与ScrollView都具有滚动能力,对于这样的View控件,当ScrollView与ListView相互嵌套会成为一种问题: 问题一:ScrollView与ListView嵌 ...

  10. js执行顺序

    我们知道有个全局的 window对象,js的一切皆window上的属性和方法.window上有个window.document属性,记录了整个html的dom树,document是顶层. body 和 ...