博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
找到n中最小的k个数
阅读量:6896 次
发布时间:2019-06-27

本文共 2694 字,大约阅读时间需要 8 分钟。

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

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

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

解法:

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

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

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

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

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

1 int findKthByHeap(int arr[], int n, int k) {2     make_heap(arr, arr + n, greater
()); 3 for (int i = 0; i < k - 1; ++i) {4 pop_heap(arr, arr + n, greater
());5 n--;6 }7 return arr[0];8 }

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]仍然可以安全地被替代。

1 int partition2(int arr[], int n) { 2     int l = 0; 3     for (int i = 1; i < n; ++i) { 4         if (arr[i] < arr[0]) { 5             swap(arr[++l], arr[i]); 6         } 7     } 8     swap(arr[0], arr[l]); 9     return l;10 }11 12 int partition(int arr[], int n) {13     int l = 0, r = n;14     while (true) {15         while (++l < n && arr[l] < arr[0]);16         while (arr[--r] > arr[0]);17         if (l >= r) break;18         swap(arr[l], arr[r]);19     }20     swap(arr[0], arr[r]);21     return r;22 }23 24 int partition3(int arr[], int n) {25     int l = 0, r = n - 1;26     int p = arr[0];27     while (l < r) {28         while (r > l && arr[r] >= p) r--;29         arr[l] = arr[r];30         while (r > l && arr[l] <= p) l++;31         arr[r] = arr[l];32     }33     arr[l] = p;34     return l;35 }36 37 int quickSelect(int arr[], int n, int k) {38     int p = partition3(arr, n);39     if (p == k - 1) return arr[p];40     else if (p < k - 1) {41         return quickSelect(arr + p + 1, n - p - 1, k - p - 1);42     } else {43         return quickSelect(arr, p, k);44     }45 }

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

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

 

 

转载于:https://www.cnblogs.com/linyx/p/3920867.html

你可能感兴趣的文章
HDU 1230 火星A+B
查看>>
C# foreach 为什么循环使用Foreach 效率要高
查看>>
oracle创建透明网关出现的问题
查看>>
对象和类
查看>>
分布式事务
查看>>
udp,select超时和recvfrom收不到数据原因
查看>>
将任意程序(如.bat文件)作为Windows服务运行
查看>>
【ElasticSearch篇】--ElasticSearch从初识到安装和应用
查看>>
Java命令参数说明大全
查看>>
PIE SDK创建掩膜
查看>>
(四)springmvc+mybatis+dubbo+zookeeper分布式架构 整合 - maven代码结构
查看>>
SQL查询到的数据放到DataSet中
查看>>
mybatis的selectOne和selectList没有数据返回时的问题
查看>>
批处理+组策略 实现规定时间段无法开机and定时关机
查看>>
我的升级
查看>>
centos 6.4 server 安装nginx
查看>>
JS OOP -04 JS中的公有成员,私有成员和静态成员
查看>>
Elevator
查看>>
nx-admin 引入Ueditor
查看>>
npm包的安装与卸载命令行总结
查看>>