partition算法可以应用在快速排序算法中,也可以应用到 Selection algorithm(在无序数组中寻找第K大的值)

Partition 实现

快速排序中用到的 partition 算法思想很简单,首先从无序数组中选出枢轴点 pivot,然后通过一趟扫描,以 pivot 为分界线将数组中其他元素分为两部分,使得左边部分的数小于等于枢轴,右边部分的数大于等于枢轴(左部分或者右部分都可能为空),最后返回枢轴在新的数组中的位置。

Partition 的一个直观简单实现如下(这里取数组的第一个元素为pivot):

// Do partition in arr[begin, end), with the first element as the pivot.
int partition(vector<int>&arr, int begin, int end){
int pivot = arr[begin];
// Last position where puts the no_larger element.
int pos = begin;
for(int i=begin+; i!=end; i++){
if(arr[i] <= pivot){
pos++;
if(i!=pos){
swap(arr[pos], arr[i]);
}
}
}
swap(arr[begin], arr[pos]);
return pos;
}

这种实现思路比较直观,但是其实并不高效。从直观上来分析一下,每个小于pivot的值基本上(除非到现在为止还没有遇见大于pivot的值)都需要一次交换,大于pivot的值(例如上图中的数字9)有可能需要被交换多次才能到达最终的位置。

如果我们考虑用 Two Pointers 的思想,保持头尾两个指针向中间扫描,每次在头部找到大于pivot的值,同时在尾部找到小于pivot的值,然后将它们做一个交换,就可以一次把这两个数字放到最终的位置。一种比较明智的写法如下:

int partition(vector<int>&arr, int begin, int end)
{
int pivot = arr[begin];
while(begin < end)
{
while(begin < end && arr[--end] >= pivot);
arr[begin] = arr[end];
while(begin < end && arr[++begin] <= pivot);
arr[end] = arr[begin];
}
arr[begin] = pivot;
return begin;
}

Partition 应用

我们都知道经典的快速排序就是首先用 partition 将数组分为两部分,然后分别对左右两部分递归进行快速排序,过程如下:

void quick_sort(vector<int> &arr, int begin, int end){
if(begin >= end - ){
return;
}
int pos = partition(arr, begin, end);
quick_sort(arr, begin, pos);
quick_sort(arr, pos+, end);
}

虽然快排用到了经典的分而治之的思想,但是快排实现的前提还是在于 partition 函数。正是有了 partition 的存在,才使得可以将整个大问题进行划分,进而分别进行处理。

除了用来进行快速排序,partition 还可以用 O(N) 的平均时间复杂度从无序数组中寻找第K大的值。和快排一样,这里也用到了分而治之的思想。首先用 partition 将数组分为两部分,得到分界点下标 pos,然后分三种情况:

  • pos == k-1,则找到第 K 大的值,arr[pos];
  • pos > k-1,则第 K 大的值在左边部分的数组。
  • pos < k-1,则第 K 大的值在右边部分的数组。

下面给出基于迭代的实现(这里寻找第 k 小的数字):

int find_kth_number(vector<int> &arr, int k){
int begin = , end = arr.size();
assert(k> && k<=end);
int target_num = ;
while (begin < end){
int pos = partition(arr, begin, end);
if(pos == k-){
target_num = arr[pos];
break;
}
else if(pos > k-){
end = pos;
}
else{
begin = pos + ;
}
}
return target_num;
}

该算法的时间复杂度是多少呢?考虑最坏情况下,每次 partition 将数组分为长度为 N-1 和 1 的两部分,然后在长的一边继续寻找第 K 大,此时时间复杂度为 O(N^2 )。不过如果在开始之前将数组进行随机打乱,那么可以尽量避免最坏情况的出现。而在最好情况下,每次将数组均分为长度相同的两半,运行时间 T(N) = N + T(N/2),时间复杂度是 O(N)。

在最好情况下,假设平均分成两半,那么kth元素要不在前一半,要不在后一半,只需要在其中一半寻找即可。所以期望复杂度是:O(n) + O(n/2) + O(n/4) + ... + O(1) = O(2n) = O(n)  (n+n/2+n/4+...+1可以用等比数列求和公式 = 2n)

参考:

https://selfboot.cn/2016/09/01/lost_partition/

基于partition的递归的更多相关文章

  1. kafka学习(四)-Topic & Partition

    topic中partition存储分布 Topic在逻辑上可以被认为是一个queue.每条消费都必须指定它的topic,可以简单理解为必须指明把这条消息放进哪个queue里.为了使得 Kafka的吞吐 ...

  2. 翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇

    原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...

  3. kafka Topic 与 Partition

    Topic在逻辑上可以被认为是一个queue队列,每条消息都必须指定它的topic,可以简单理解为必须指明把这条消息放进哪个queue里.为 了使得Kafka的吞吐率可以水平扩展,物理上把topic分 ...

  4. kafka partition(分区)与 group

    kafka partition(分区)与 group   一. 1.原理图 2.原理描述 一个topic 可以配置几个partition,produce发送的消息分发到不同的partition中,co ...

  5. kafka partition(分区)与 group(转)

    原文  https://www.cnblogs.com/liuwei6/p/6900686.html 一. 1.原理图 2.原理描述 一个topic 可以配置几个partition,produce发送 ...

  6. Spark中的partition和block的关系

    hdfs中的block是分布式存储的最小单元,类似于盛放文件的盒子,一个文件可能要占多个盒子,但一个盒子里的内容只可能来自同一份文件.假设block设置为128M,你的文件是250M,那么这份文件占3 ...

  7. 超级账本Hyperledge的kafka共识算法里的Topic 与 Partition

    Topic在逻辑上可以被认为是一个queue队列,每条消息都必须指定它的topic,可以简单理解为必须指明把这条消息放进哪个queue里.为 了使得Kafka的吞吐率可以水平扩展,物理上把topic分 ...

  8. 个推基于 Apache Pulsar 的优先级队列方案

    作者:个推平台研发工程师 祥子 一.业务背景在个推的推送场景中,消息队列在整个系统中占有非常重要的位置.当 APP 有推送需求的时候, 会向个推发送一条推送命令,接到推送需求后,我们会把APP要求推送 ...

  9. Kafka深度解析(如何在producer中指定partition)(转)

    原文链接:Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅的消息系统.主要设计目标如下: 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能 ...

随机推荐

  1. python 粘包现象

    一. 粘包现象 1. 粘包现象的由来(1)TCP属于长连接,当服务端与一个客户端进行了连接以后,其他客户端需要(排队)等待.若服务端想要连接另一个客户端,必须首先断开与第一个客户端的连接. (2)缓冲 ...

  2. jQuery代码书写规范

    1. 对于同一个对象不超过三个操作的,可以直接写成一行 $("li").show().unbind("click"); 2. 对于同一个对象的较多操作,建议每行 ...

  3. springboot使用elasticsearch的客户端操作eslaticsearch

    一  ES客户端 ES提供多种不同的客户端: 1.TransportClient ES提供的传统客户端,官方计划8.0版本删除此客户端. 2.RestClient RestClient是官方推荐使用的 ...

  4. 解MySQL基准测试和sysbench工具

    前言 作为一名后台开发,对数据库进行基准测试,以掌握数据库的性能情况是非常必要的.本文介绍了MySQL基准测试的基本概念,以及使用sysbench对MySQL进行基准测试的详细方法. 文章有疏漏之处, ...

  5. poco编译与运行

    1.引言 Poco C++库是: 一系列C++类库,类似Java类库,.Net框架,Apple的Cocoa; 侧重于互联网时代的网络应用程序 使用高效的,现代的标准ANSI/ISO C++,并基于ST ...

  6. IDEA安装maven

    1.先到maven的官网下载安装包:http://maven.apache.org/download.cgi 解压安装包 2.配置环境变量 新建变量名MAVEN_HOME 变量值    D:\Soft ...

  7. [转帖]nginx配置ssl证书实现https访问

    https://www.cnblogs.com/tianhei/p/7726505.html 今天就是如此处理的 感觉挺不错的. 一,环境说明 服务器系统:ubuntu16.04LTS 服务器IP地址 ...

  8. 【Python】【demo实验14】【练习实例】【斐波那契数列】【经典兔子生小兔子问题】

    古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少? 每个月的兔子数量 1:22:23:4 2+24:6 2+ ...

  9. spark的安装步骤

    官网:http://spark.apache.org/downloads.html 安装:tar -zxvf spark-2.4.3-bin-hadoop2.7.tgz#配置环境变量(vim ~/.b ...

  10. X86逆向11:F12暂停法的妙用

    本节课将介绍F12暂停法的使用技巧,F12暂停法的原理其实很简单,当我们点击OD中的暂停按钮时,OD会将当前的堆栈状态保存起来,并暂停当前窗体的线程执行,直到我们点击运行按钮OD才会唤醒全部线程并继续 ...