打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
★经典问题—元素选择问题

元素选择问题 : 给定线性序集中n个元素和一个整数k(1<=k<=n),要求找出这n个元素中第k小的元素(第n-k大)。这一问题可以演化成找最大最小值、找中位数等。

最简单思想:如果是直接找最大最小值,则可以通过N次比较来完成,其时间复杂度为O(N),空间复杂度为O(1)。除此之外,对于一般的k值,可以考虑对序列N先进行排序,然后直接定位第k个位置上的数即可。时间复杂度最好为O(N*logN)。

改进思想:

(1) 在某些特殊情况下,是很容易设计出O(N)的算法。比如最大最小值的时候。

如果k<=n/logn 找第k小的元素。我们可以通过堆排序的方法。首先建立小顶堆,其时间复杂度为O(N)。然后每次输出堆顶元素(当前堆的最小值)后调整堆顶,其时间复杂度为O(logN)。循环k次,当第k次输出堆顶时结束。这样的时间复杂度为O(N+k*logN), 而k<=n/logn,即k接近于常数,则时间复杂度近似为O(N)。

如果k>=n-n/logn 找第k小的元素。同理可以建立一个(n-k)次输出的大顶堆即可。

当k的大小靠近n的两侧时,比如n=10,k=2或8。我们可以同归堆排序来达到近似O(N)的时间复杂度。

(2) 但一般的k值,特别是中位数的选择问题似乎就比上一种情况要难了。但事实上,我们仍然可以在O(n)的时间内解决。

可以考虑快排的分治算法,对N序列进行一次Partition。与快排不同的是,每次只对划分后的一个子数组进行处理。其算法代码如下:

Cpp代码
  1. //在a数组中选择第k小的数
  2. Type select(Type a[], int low, int high, int k){
  3. //找到的第k小的数
  4. if(low==high) return a[low];
  5. //一次选择划分,此时a[low..mid-1]<a[mid]<a[mid+1..high]
  6. int mid=partition(a,low, high);
  7. int midSize=mid-low+1;
  8. if(k<=midSize) return select(a, low, mid, k);
  9. else return select(a, mid+1, high, k-midSize);
  10. }

从代码中很容易看出,在最坏情况下,这种算法和快排有着一样的蜕化现象,需要O(N^2)时间复杂度。但是在一般情况下,平均性能上可以保证近似O(N)的时间复杂度。如果想要克服最坏情况,其基准(快排中的枢轴)的选择可以进行优化。使得基准所划分出来的两个子数组的长度至少为原数组长度的x倍(0<x<1)。具体优化策略可以参见《【JDK优化策略】java.util.Arrays的排序研究》中的快排优化。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
【编程之美】读书笔记:寻找最大的K个数[转帖]
看动画轻松理解时间复杂度(二)
给定两个排好序的整型数组,怎么判断它们是否含有相同的数字?
判断两个数组中是否有相同的数字
二分查找法的实现和应用汇总
求一个数组中第k大的数方法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服