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. Win7下Maven的安装与配置

    简介  官网:https://maven.apache.org/ Apache Maven,是一个(特别是Java软件)项目管理及自动构建工具,由Apache软件基金会所提供.基于项目对象模型(Pro ...

  2. 重启Apache报错apache2: Could not reliably determine the server's fully qualified domain name, using 127.0.1.1 for ServerName ... waiting的解决方法

    启动apache提示 : apache2: Could not reliably determine the server's fully qualified domain name, using 1 ...

  3. fatal error: openssl/sha.h: No such file or directory 解决方案

    出现这个或者fatal error: openssl/名单.h: No such file or directory.都是没有安装libssl-dev- libssl-dev包含libraries, ...

  4. WPF:linq

    /// <summary> /// 该药品是否存在发药信息 /// 存在返回true,否则返回false /// </summary> /// <param name=& ...

  5. TCP与UDP

    TCP(Transmission Control Protocol,传输控制协议)是面向连接的协议:可靠.保证正确性:顺序到达:流量控制.拥塞控制:重传机制.窗口机制:对系统资源.时间要求多:流模式S ...

  6. 你不知道的JavaScript--大白话讲解Promise

    转载:http://blog.csdn.net/i10630226/article/details/50867792 一.Promise小试 复杂的概念先不讲,我们先简单粗暴地把Promise用一下, ...

  7. Asp.net 解析json

    Asp.net Json数据解析的一种思路 http://www.cnblogs.com/scy251147/p/3317366.html http://tools.wx6.org/json2csha ...

  8. Quoted-printable 编码介绍、编码解码转换

    求教,“=B9=A4=D7=F7=BC=F2=B1=A8” 这种是什么编码方式? Quoted-printable 可译为“可打印字符引用编码”.“使用可打印字符的编码”,我们收邮件,查看信件原始信息 ...

  9. Objective-C:Foundation框架-概述

    iOS的整体架构(以iOS8为例)图如下: 从Cocoa Touch到Core OS下面四层包含了开发iOS应用程序所用到的所有API(第三方框架也是基于这几个层的).每个层又都包含了许多框架.框架就 ...

  10. 《Play for Java》学习笔记(五)Form

    本书第六章以一个实例介绍了Play Framework中Form的使用,如何绑定数据,如何进行验证 一.项目结构和action