今天写一个多路快排函数模板,与STL容器兼容的。

我们默认为升序排序

因为,STL容器均为逾尾容器,所以我们这里采用的参数也是逾尾的参数

一、二路快排

基本思路

给你一个序列,先选择一个数作为基数,我们要做的是把小于该基数的数字放于左侧,大于该基数的数字放于右侧,最后将此基数放于中间,形成新的序列,我们把左侧序列和右侧序列分别像之前那样做,最后得到的序列即为顺序序列。

过程演示

比如给你一个序列   4  3  1  5  4  7  9  1  8  0

我们采用双指针扫描法,红色代表左指针所指位置,绿色代表右指针所指位置

我们选取一个数作为基数,假设选第一个数,即 flag  为  4

那么,我们把这个位置空出来,用null表示

则,一开始的序列图为

null  3  1  5  4  7  9  1  8  0                 flag  is  4

我们从右指针开始遍历,找到第一个小于flag的数字,填入坑中,然后左指针++,指向下一位然后开始遍历

0    1  5  4  7  9  1  8  null                

0  3    5  4  7  9  1  8  null         

0  3  1    4  7  9  1  8  null    

0  3  1  null  4  7  9  1     

0  3  1  null  4  7  9    8   

0  3  1  1    7  9  null  8   

0  3  1  1  4  7  9  null  8   

0  3  1  1  4  null    7  8   

右指针指向9,9>flag,继续左移,然后红绿指针相遇,结束

我们把flag放入null,即形成了新的序列

0  3  1  1  4  4  9  7  8  5

我们把左侧序列0  3  1  1  4

和右侧序列9  7  8  5

分别如上做,即可得到有序序列

C++模板代码:

template<typename value_type, typename value_Ptr>
void quick_sort(value_Ptr begin, value_Ptr end) //逾尾
{
if (begin == end)return; value_type flag = *begin; value_Ptr l = begin, r = end;
r--;
while (l < r)
{
while (l < r && *r > flag)r--;
if (l < r)*(l++) = *r;
while (l < r && *l < flag)l++;
if (l < r)*(r--) = *l;
}
*l = flag;
quick_sort<value_type>(begin, l);
quick_sort<value_type>(l + , end);
}

测试以及使用

#include <iostream>
#include <vector>
using namespace std; int main()
{
ios::sync_with_stdio(false); int list[]{ ,,,,,,,,, };
vector<int> v{ list,list + }; quick_sort<int>(list + , list + );
quick_sort<int>(v.begin(), v.end());
for (auto it : v)cout << it << " ";
cout << endl;
for (auto it : list)cout << it << " ";
cout << endl; char list_[]{ 'r','c','','A','z','b','','','r', '' };
vector<char>v_{ list_,list_ + }; quick_sort<char>(list_ + , list_ + );
quick_sort<char>(v_.begin(), v_.end());
for (auto it : v_)cout << it << " ";
cout << endl;
for (auto it : list_)cout << it << " ";
cout << endl;
}

测试结果

效率为O(N * logN)

如果序列中有很多重复的元素,那么,那一长串的重复值就相当于一个非递减序列,按理说是不需要排序的,但是上述无法识别此情况,它依旧会把这一长串不必处理的序列进行处理,无疑将函数执行效率大打折扣

这时,我们就需要引入三路快排

二、三路快排

基本思路

给你一个序列,我们选择一个基数作为标准,小于基数的元素放于左侧区间,等于基数的元素放于中间区间,大于基数的元素放于右侧区间

整理好之后,我们将左侧区间和右侧区间如上做。

过程描述

我们用四指针扫描

l  r  用于扫描待处理的序列

equ_l   equ_r  用于记录两侧相等的区间

equ_l  = l = begin

r 和equ_r指向右侧

让l r向中间遍历,遇到不等于的情况和快排一样,留一个空位,依次填补即可,遇到相等的情况,左指针指向的值等于基数,那么就放于equ_l处,且equ_l++,右边同理

当l  r  相遇时,我们让四指针分别向两端移动,equ_l 所指的元素依次和 l 所指的元素交换,右侧同理,即可得到我们想要的序列,即  <  =  >

之后,我们将<区间和>区间分别如上做,即可得到有序序列

泛型模板代码:

template<typename value_type, typename value_Ptr>
void Tri_quick(const value_Ptr& begin, const value_Ptr& end)
{
static auto swap = [](value_type& l, value_type& r)
{
if (l == r)return;
value_type t{ l };
l = r;
r = t;
};
value_Ptr equ_l = begin, equ_r = end, l = begin, r = end;
if (begin >= end || begin >= end - )return; //上式:如果第二个条件成立的话,表达式1中的 ++l 就会解析 end 迭代器
value_type flag = *l;
while (l < r)
{
while (l < r && *(++l) < flag)if (l == end - )break; // 表达式1
while (l < r && flag < *(--r));
if (*l != *r) swap(*l, *r);
if (*l == flag) swap(*(++equ_l), *l);
if (*r == flag) swap(*(--equ_r), *r);
}
if (*l > flag)l--;
//此时l==r,可能l的值大于flag,我们不换,但是前面的一定小于等于flag,所以我们--
while (equ_l > begin)swap(*(equ_l--), *(l--));
//选择大于是因为equ_l如果为容器的begin迭代器,那么不允许--操作
swap(*equ_l, *l);
//所以begin迭代器另做处理
while (equ_r < end)swap(*(equ_r++), *(r++));
Tri_quick<value_type>(begin, l);
Tri_quick<value_type>(r, end);
}

测试及使用

#include <iostream>
#include <vector>
using namespace std; int main()
{
ios::sync_with_stdio(false);
int list[]{ ,,,,,,,,, };
vector<int> v{ list,list + };
Tri_quick<int>(list + , list + );
for (auto it : list)cout << it << " ";
cout << endl;
Tri_quick<int>(v.begin(), v.end());
for (auto it : v)cout << it << " ";
cout << endl; char list_[]{ 'r','c','','A','z','b','','','r', '' };
vector<char>v_{ list_,list_ + }; Tri_quick<char>(list_ + , list_ + );
Tri_quick<char>(v_.begin(), v_.end());
for (auto it : v_)cout << it << " ";
cout << endl;
for (auto it : list_)cout << it << " ";
cout << endl;
}

注:三路快排中即使没有重复元素,那也最差退化到二路快排的效率,效率不会低于二路快排

这就是今天的多路快排,关于快排有很多不适应的情况,也有很多优化的算法,关于其他的算法,大家可以去其他地方学习,这里不做赘述

如果有什么问题,请于下方评论区留言。

感谢您的阅读,生活愉快~

<泛> 多路快排的更多相关文章

  1. Java常见的几种排序算法-插入、选择、冒泡、快排、堆排等

    本文就是介绍一些常见的排序算法.排序是一个非常常见的应用场景,很多时候,我们需要根据自己需要排序的数据类型,来自定义排序算法,但是,在这里,我们只介绍这些基础排序算法,包括:插入排序.选择排序.冒泡排 ...

  2. 网站快速收录/站点快速收录/seo快排技术/seo快速排名/泛域名快速收录/泛目录快速收录

    泛目录技术是目前最快速最有效的办法,增加站点的收录方面,这里推荐莲花泛目录 莲花泛目录,完善的技术支持,代码亲自编写,独立研发.业界领先. (这个是seo关键词,请无视,直接看下面的泛目程序介绍)网站 ...

  3. F#之旅4 - 小实践之快排

    参考文章:https://swlaschin.gitbooks.io/fsharpforfunandprofit/content/posts/fvsc-quicksort.html F#之旅4 - 小 ...

  4. 快排 快速排序 qsort quicksort C语言

    现在网上搜到的快排和我以前打的不太一样,感觉有点复杂,我用的快排是FreePascal里/demo/text/qsort.pp的风格,感觉特别简洁. #include<stdio.h> # ...

  5. iOS常见算法(二分法 冒泡 选择 快排)

    二分法: 平均时间复杂度:O(log2n) int halfFuntion(int a[], int length, int number)  { int start = 0; int end = l ...

  6. C++ 快排

    // 进行一轮快排并返回当前的中间数 int getMiddle( int* arr, int low, int high ) { auto swaparr = [&]( int i, int ...

  7. 先贴上代码:Random快排,快排的非递归实现

    设要排序的数组是A[0]……A[N-1],首先任意选取一个数据(通常选用数组的第一个数)作为主元,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序.值得注意的是, ...

  8. ACM/ICPC 之 快排+归并排序-记录顺序对(TSH OJ-LightHouse(灯塔))

    TsingHua OJ 上不能使用<algorithm>头文件,因此需要手写快排(刚开始写的时候自己就出了很多问题....),另外本题需要在给横坐标排序后,需要记录纵坐标的顺序对的数量,因 ...

  9. 数组第K小数问题 及其对于 快排和堆排 的相关优化比较

    题目描述 给定一个整数数组a[0,...,n-1],求数组中第k小数 输入描述 首先输入数组长度n和k,其中1<=n<=5000, 1<=k<=n 然后输出n个整形元素,每个数 ...

随机推荐

  1. jQuery UI dialog 使用记录

    1 属性 1.11 autoOpen ,这个属性为true的时候dialog被调用的时候自动打开dialog窗口.当属性为false的时候,一开始隐藏窗口,知道.dialog("open&q ...

  2. CodeForces 816C 思维

    On the way to school, Karen became fixated on the puzzle game on her phone! The game is played as fo ...

  3. hdu 1254 推箱子(双重bfs)

    题目链接 Problem Description 推箱子是一个很经典的游戏.今天我们来玩一个简单版本.在一个M*N的房间里有一个箱子和一个搬运工,搬运工的工作就是把箱子推到指定的位置,注意,搬运工只能 ...

  4. 定价(Price)

    传送门 [题目描述] 在市场上有很多商品的定价类似于 999 元.4999 元.8999 元这样.它们和 1000 元.5000 元和 9000 元并没有什么本质区别,但是在心理学上会让人感觉便宜很多 ...

  5. python初步学习-练习题

    1.实现1-100的所有的和 #!/usr/bin/env python #encoding:utf8 '''实现1-100的所有的和 1. 使用列表解析获取0-100的列表 2. 使用reduce内 ...

  6. MongoDB警告信息

    更多内容推荐微信公众号,欢迎关注: MongoDB警告信息: 1. WARNING: Using the XFS filesystem is strongly recommended with the ...

  7. Hibernate5笔记8--Hibernate事务相关内容

    Hibernate事务相关内容: (1) 事务四大特性(简称ACID): (1)原子性(Atomicity) 事务中的全部操作在数据库中是不可分割的,要么全部完成,要么均不执行. (2)一致性(Con ...

  8. VC++6.0中ClassView中类消失 解决方案[转自网络]

    有时候在VC++6.0中编程会出现这样一个问题,由于对C...View类的操作后,在窗口左边ClassView框中的C...View类会消失,这种操作通常是在C...View类中右击点“Add Win ...

  9. LVS ARP广播产生的问题和处理方式【转】

    转自 LVS ARP广播产生的问题和处理方式-htckiller2010-ChinaUnix博客http://blog.chinaunix.net/uid-24960107-id-193084.htm ...

  10. Windows版Oracle重建EM---备注

    前提条件添加环境变量 ORACLE_HOSTNAME=<主机名:如:DESKTOP-P6J1a>ORACLE_SID=orclORACLE_UNQNAME=orcl 执行删除命令 C:\U ...