Select 问题: 在一个无序的数组中 找到第 n 大的元素。

思路 1: 排序,O(NlgN)

思路 2: 利用快排的 RandomizedPartition(), 平均复杂度是 O(N)

思路 3:    同样是利用快排的 Partition(), 但是选择 pivot 的时候不是采用随机,而是通过一种特殊的方法。从而使复杂度最坏情况下是 O(N)。

本文介绍 STL 算法库中 nth_elemnt 的实现代码。

STL 采用的算法是: 当数组长度 <= 3时, 采用插入排序。

当长度 > 3时, 采用快排 Partition 的思想;

一、使用说明

void

nth_element (RandomAccessIteratorbeg,

RandomAccessIterator
nth
,

RandomAccessIterator
end
)

void

nth_element (RandomAccessIterator beg,

RandomAccessIterator nth,

RandomAccessIterator end,

                         BinaryPredicate op)

1. 两个函数都是让 第 n 个位置上的元素就位,

所有在位置 n 之前的元素都小于或等于它,

所有在位置 n 之后的元素都大于或等于它。

2. 复杂度: 平均复杂度是 O(N)

以下例子是使用范例:

// copyright @ L.J.SHOU Feb.23, 2014
#include <iostream>
#include <algorithm>
#include <iterator>
using namespace std; int main(void)
{
int a[]={3,5,2,6,1,4}; nth_element(a, a+3, a+sizeof(a)/sizeof(int));
cout << "The fourth element is: " << a[3] << endl; // output array a[]
copy(a, a+sizeof(a)/sizeof(int),
ostream_iterator<int>(cout, " "));
return 0;
}

程序输出结果:

The fourth element is: 4

     2 1 3 4 6 5

二、源码分析

// nth_element() and its auxiliary functions.  

template <class _RandomAccessIter, class _Tp>
void __nth_element(_RandomAccessIter __first, _RandomAccessIter __nth,
_RandomAccessIter __last, _Tp*) {
while (__last - __first > 3) {
_RandomAccessIter __cut =
__unguarded_partition(__first, __last,
_Tp(__median(*__first,
*(__first + (__last - __first)/2),
*(__last - 1))));
if (__cut <= __nth)
__first = __cut;
else
__last = __cut;
}
__insertion_sort(__first, __last);
} template <class _RandomAccessIter>
inline void nth_element(_RandomAccessIter __first, _RandomAccessIter __nth,
_RandomAccessIter __last) {
__STL_REQUIRES(_RandomAccessIter, _Mutable_RandomAccessIterator);
__STL_REQUIRES(typename iterator_traits<_RandomAccessIter>::value_type,
_LessThanComparable);
__nth_element(__first, __nth, __last, __VALUE_TYPE(__first));
}
template <class _RandomAccessIter, class _Tp>
_RandomAccessIter __unguarded_partition(_RandomAccessIter __first, 
                                        _RandomAccessIter __last, 
                                        _Tp __pivot) 
{
  while (true) {
    while (*__first < __pivot)
      ++__first;
    --__last;
    while (__pivot < *__last)
      --__last;
    if (!(__first < __last))
      return __first;
    iter_swap(__first, __last);
    ++__first;
  }

_unguarded_partition 就是快排的 partition, 将数组分成两部分,左边的元素都小于或者等于 pivot, 右边的元素都大于或者等于 pivot.

从上述代码可以看出, nth_element 采用的 pivot 是 首元素,尾元素,中间元素,三个数的median.

通过_unguarded_partition 将数组分成两部分,

如果 nth 这个迭代器在左半边,则继续在左半边搜索;

若   nth 在右半边, 则在右半边搜索;

直到数组的长度 <= 3,时, 采用插入排序。这时 nth 迭代器所指向的数就归位了,而且它的左边元素都小于或者等于它, 右边元素都大于或者等于它。

STL 源码分析《2》----nth_element() 使用与源码分析的更多相关文章

  1. Spring源码分析——BeanFactory体系之抽象类、类分析(二)

    上一篇分析了BeanFactory体系的2个类,SimpleAliasRegistry和DefaultSingletonBeanRegistry——Spring源码分析——BeanFactory体系之 ...

  2. Android7.0 Phone应用源码分析(二) phone来电流程分析

    接上篇博文:Android7.0 Phone应用源码分析(一) phone拨号流程分析 今天我们再来分析下Android7.0 的phone的来电流程 1.1TelephonyFramework 当有 ...

  3. Android7.0 Phone应用源码分析(一) phone拨号流程分析

    1.1 dialer拨号 拨号盘点击拨号DialpadFragment的onClick方法会被调用 public void onClick(View view) { int resId = view. ...

  4. jQuery 源码分析和使用心得 - 关于源码

    说到jQuery, 大家可能直觉的认为jQuery的源码应该就是一个jquery.xx.js这样的一个文件. 但是看到真正的源码的时候, 整个人都思密达了.jQuery的源码做的事远比你想象的多, 为 ...

  5. Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Job Manager 启动

    Job Manager 启动 https://t.zsxq.com/AurR3rN 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Mac ...

  6. Flink 源码解析 —— Standalone Session Cluster 启动流程深度分析之 Task Manager 启动

    Task Manager 启动 https://t.zsxq.com/qjEUFau 博客 1.Flink 从0到1学习 -- Apache Flink 介绍 2.Flink 从0到1学习 -- Ma ...

  7. Tomcat源码分析一:编译Tomcat源码

    Tomcat源码分析一:编译Tomcat源码 1 内容介绍 在之前的<Servlet与Tomcat运行示例>一文中,给大家带来如何在Tomcat中部署Servlet应用的相关步骤,本文将就 ...

  8. Android源码分析(十一)-----Android源码中如何引用aar文件

    一:aar文件如何引用 系统Settings中引用bidehelper-1.1.12.aar 文件为例 源码地址:packages/apps/Settings/Android.mk LOCAL_PAT ...

  9. Android源码分析(四)-----Android源码编译及刷机步骤

    一 : 获取源码: 每个公司服务器地址不同,以如下源码地址为例: http://10.1.14.6/android/Qualcomm/msm89xx/branches/msm89xx svn环境执行: ...

  10. linux2.4.0源码下载地址(配合毛德操情景分析)

    https://www.kernel.org/pub/linux/kernel/v2.4/

随机推荐

  1. Vim学习指南

    你想尽可能快地自学vim(为大家所熟知的最好的编辑器) .这是我学习的方法:从细处入手然后慢慢掌握所有技巧. Vim 六十亿美元的编辑器 设计优良,强壮,快速. 学习vim并把它作为你的下一个文本编辑 ...

  2. python list对象

    list对象 1.list定义l=['first','second'] 2.list追加对象list.append('aa');append的方法总是把元素追加到末尾 insert(索引号,'项目') ...

  3. SSH基础(2)

    linux下配置 ssh运行的参数详解:

  4. Java 流笔记

    BufferedReader 从字符输入流中读取文本,缓冲各个字符,从而实现字符.数组和行的高效读取.操作: Reader BufferedWriter 将文本写入字符输出流,缓冲各个字符,从而提供单 ...

  5. Thread 总结

    进程:是一个正在执行的程序 每一个进程执行都有一个执行顺序.该顺序是一个执行路劲,后者叫一个控制单元. 线程:就是进程中的一个独立控制单元. 线程在控制着进程的执行 一个进程中至少有个一个线程 Jav ...

  6. BZOJ3696 化合物

    我们可以树形dp... 令f[p][d]表示以p为根的子树,与p距离为d的结点数 然后我们计算答案: 一种是从某个节点q到根p的方案,对和为d的贡献是1 另一种是p的一个子树中的节点x到另一个子树中的 ...

  7. hduacm 2888 ----二维rmq

    http://acm.hdu.edu.cn/showproblem.php?pid=2888 模板题  直接用二维rmq 读入数据时比较坑爹  cin 会超时 #include <cstdio& ...

  8. Unity中的Path对应各平台中的Path

    OS: Application.dataPath :                    Application/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/xxx.a ...

  9. hdu 4622 Reincarnation

    http://acm.hdu.edu.cn/showproblem.php?pid=4622 用字典树把每一个字符串对应成一个整数 相同的字符串对应到相同的整数上 把所用的串对应的整数放在一个数组里 ...

  10. undefined reference to typeinfo - C++ error message

    undefined reference to typeinfo - C++ error message There are some compiler and loader error message ...