基于partition的递归
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的递归的更多相关文章
- kafka学习(四)-Topic & Partition
topic中partition存储分布 Topic在逻辑上可以被认为是一个queue.每条消费都必须指定它的topic,可以简单理解为必须指明把这条消息放进哪个queue里.为了使得 Kafka的吞吐 ...
- 翻译连载 | 第 9 章:递归(下)-《JavaScript轻量级函数式编程》 |《你不知道的JS》姊妹篇
原文地址:Functional-Light-JS 原文作者:Kyle Simpson-<You-Dont-Know-JS>作者 关于译者:这是一个流淌着沪江血液的纯粹工程:认真,是 HTM ...
- kafka Topic 与 Partition
Topic在逻辑上可以被认为是一个queue队列,每条消息都必须指定它的topic,可以简单理解为必须指明把这条消息放进哪个queue里.为 了使得Kafka的吞吐率可以水平扩展,物理上把topic分 ...
- kafka partition(分区)与 group
kafka partition(分区)与 group 一. 1.原理图 2.原理描述 一个topic 可以配置几个partition,produce发送的消息分发到不同的partition中,co ...
- kafka partition(分区)与 group(转)
原文 https://www.cnblogs.com/liuwei6/p/6900686.html 一. 1.原理图 2.原理描述 一个topic 可以配置几个partition,produce发送 ...
- Spark中的partition和block的关系
hdfs中的block是分布式存储的最小单元,类似于盛放文件的盒子,一个文件可能要占多个盒子,但一个盒子里的内容只可能来自同一份文件.假设block设置为128M,你的文件是250M,那么这份文件占3 ...
- 超级账本Hyperledge的kafka共识算法里的Topic 与 Partition
Topic在逻辑上可以被认为是一个queue队列,每条消息都必须指定它的topic,可以简单理解为必须指明把这条消息放进哪个queue里.为 了使得Kafka的吞吐率可以水平扩展,物理上把topic分 ...
- 个推基于 Apache Pulsar 的优先级队列方案
作者:个推平台研发工程师 祥子 一.业务背景在个推的推送场景中,消息队列在整个系统中占有非常重要的位置.当 APP 有推送需求的时候, 会向个推发送一条推送命令,接到推送需求后,我们会把APP要求推送 ...
- Kafka深度解析(如何在producer中指定partition)(转)
原文链接:Kafka深度解析 背景介绍 Kafka简介 Kafka是一种分布式的,基于发布/订阅的消息系统.主要设计目标如下: 以时间复杂度为O(1)的方式提供消息持久化能力,即使对TB级以上数据也能 ...
随机推荐
- 【Struts中private static final long serialVersionUID的作用】
private static final long serialVersionUID = -1672970955045193907L; SerialVersionUID,后面简称SUID 其实序 ...
- Altera DDR2控制器学习笔记
Altera DDR2控制器使用IP的方式实现,一般很少自己写控制器代码. ddr22 ddr22_inst ( .aux_full_rate_clk (mem_aux_full_rate_clk), ...
- new URLSearchParams( )用法说明
URLSearchParams 接口定义了一些实用的方法来处理 URL 的查询字符串. 方法:该接口不继承任何属性. URLSearchParams.append() 插入一个指定的键/值对作为新的搜 ...
- 安装glance
在控制节点上执行 controllerHost='controller' MYSQL_PASSWD='m4r!adbOP' GLANCE_PASSWD='glance1234!' 1.创建数据库 my ...
- python之xml数据解析
因为项目需求需要查询一些网站的ALEXA排名,百度后得到的方法是,访问http://data.alexa.com/data?cli=10&dat=snbamz&url=%YOURURL ...
- 使用国内镜像给ROS安装提速
大半年没写博客了.最近有幸参与机器人导航研究,在学习ROS相关知识.在安装ROS时使用国外的官方源安装非常慢.这里把使用国内镜像的安装方式做一个记录. 笔者用的是Kinetic版本,操作系统是Ubun ...
- 整合AD RMS与EX 2010。
1.点击开始菜单, 选择所有程 序,展开 Mi cros oft Excha nge Server 2010 ,打开Excha nge Ma na gement Cons ol e,选择收件人配 ...
- Linux正则表达式题型
1.将下面的/etc/passwd所有行的第一列和最后一列相互调换位置. 解答: 1)使用sed的后向引用 2)awk -F ":" '{print $7":" ...
- 第五周课程总结&实验报告(四)
第五周课程总结 本周主要学习了 1.抽象类 抽象类的定义格式 abstract class抽象类名称{ 属性; 访问权限返回值类型方法名称(参数){ //普通方法 [return返回值]; } 访问权 ...
- 人渣给我笔记本网络适配器里加的的“bluetooth设备(个人局域网)”,卸载不掉