三向切分的快速排序

快速排序在实际应用中会面对大量具有重复元素的数组。例如加入一个子数组全部为重复元素,则对于此数组排序就可以停止,但快排算法依然将其切分为更小的数组。这种情况下快排的性能尚可,但存在着巨大的改进潜力。(从O(nlgn)提升到O(n))

一个简单的想法就是将数组分为三部分:小于当前切分元素的部分,等于当前切分元素的部分,大于当前切分元素的部分。

E.W.Dijlstra(对,就是Dijkstra最短路径算法的发明者)曾经提出一个与之相关的荷兰国旗问题(一个数组中有分别代表红白蓝三个颜色的三个主键值,将三个主键值排序,就得到了荷兰国旗的颜色排列)。

他提出的算法是: 对于每次切分:从数组的左边到右边遍历一次,维护三个指针,其中lt指针使得元素(arr[0]-arr[lt-1])的值均小于切分元素;gt指针使得元素(arr[gt+1]-arr[N-1])的值均大于切分元素;i指针使得元素(arr[lt]-arr[i-1])的值均等于切分元素,(arr[i]-arr[gt])的元素还没被扫描,切分算法执行到i>gt为止。每次切分之后,位于gt指针和lt指针之间的元素的位置都已经被排定,不需要再去处理了。之后将(lo,lt-1),(gt+1,hi)分别作为处理左子数组和右子数组的递归函数的参数传入,递归结束,整个算法也就结束。

三向切分的示意图:

C++代码如下:

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 #define maxn 10000
 5 int a[maxn];
 6
 7 void exchange( int i,int j )
 8 {
 9     int tmp=a[i];
10     a[i]=a[j];
11     a[j]=tmp;
12 }
13
14
15 void qsort3way ( int lo,int hi )
16 {
17     if( lo>=hi ) return;  //单个元素或者没有元素的情况
18     int lt=lo;
19     int i=lo+1;  //第一个元素是切分元素,所以指针i可以从lo+1开始
20     int gt=hi;
21     int v=a[lo];
22     while( i<=gt )
23     {
24         if( a[i]<v )  //小于切分元素的放在lt左边,因此指针lt和指针i整体右移
25             exchange( lt++,i++ );
26         else if ( a[i]>v )  //大于切分元素的放在gt右边,因此指针gt需要左移
27             exchange( i,gt-- );
28         else
29             i++;
30     }
31     //lt-gt的元素已经排定,只需对it左边和gt右边的元素进行递归求解
32     qsort3way( lo,lt-1 );
33     qsort3way( gt+1,hi );
34 }
35
36
37 int main()
38 {
39     int n;
40     cin>>n;
41     for( int i=0; i<n; i++  )
42         cin>>a[i];
43     qsort3way( 0,n-1 );
44     for( int i=0; i<n; i++  )
45         cout<<a[i];
46     cout<<endl;
47     return 0;
48 }

下面是《算法(第四版)》上对算法切分轨迹的一个示例说明:

对于包含大量重复元素的数组,这个算法将排序时间从线性对数级降到了线性级别。

时间: 2024-10-10 11:11:31

三向切分的快速排序的相关文章

Quick Sort(三向切分的快速排序)(Java)

1 //三向切分的快速排序 2 //这种切分方法对于数组中有大量重复元素的情况有比较大的性能提升 3 4 public static void main(String[] args) 5 { 6 Scanner input = new Scanner(System.in); 7 int n = input.nextInt(); 8 int[] a = new int[n]; 9 10 for(int i = 0; i < a.length; i++) 11 a[i] = (int)(Math.r

快速排序算法原理及实现(单轴、三向切分、双轴)

欢迎探讨,如有错误敬请指正 如需转载,请注明出处http://www.cnblogs.com/nullzx/ 1. 单轴快速排序的基本原理 快速排序的基本思想就是从一个数组中任意挑选一个元素(通常来说会选择最左边的元素)作为中轴元素,将剩下的元素以中轴元素作为比较的标准,将小于等于中轴元素的放到中轴元素的左边,将大于中轴元素的放到中轴元素的右边,然后以当前中轴元素的位置为界,将左半部分子数组和右半部分子数组看成两个新的数组,重复上述操作,直到子数组的元素个数小于等于1(因为一个元素的数组必定是有

小橙书阅读指南(六)——快速排序和三向切分快速排序

算法描述:快速排序是一种分治的排序算法.它将数组分为两个子数组,并将两部分独立的排列.快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将子数组归并以将整个数组排序:而快速排序将数组排序的方式则是当两个子数组都有序时整个数组也就自然有序了. 算法图示: 算法解释:选择标的元素(5)并且便利数组,将素有小于5的元素都安排在它的左侧,而大于5的元素都安排在它的右侧.之后再通过递归的方法分别处理左边的子数组和右边的子数组. 快速排序的算法难点在于尽量不要使用额外的存储空间(即保证原地

算法(第4版)-2.3 快速排序

public class Quick { public static void sort(Comparable[] a) { StdRandom.shuffle(a); // 消除对输入的依赖 sort(a, 0, a.length - 1); } private static void sort(Comparable[] a, int lo, int hi) { if (hi <= lo) return; int j = partition(a, lo, hi); // 切分 sort(a,

算法_快速排序

快速排序是一种分治的排序算法.它将一个数组分成两个子数组,将两部分独立的排序.快速排序和归并排序是互补的:归并排序将数组分成两个子数组分别排序,并将有序的子数组归并并将整个数组排序,而快速排序将数组排序的方式则是当两个子数组都有序的时候,整个数组就自然有序了,切分是快速排序中最重要的操作.这个过程使得数组满足下面的条件: 1.对于某个j,a[j]已经排定 2.a[lo]到a[j-1]中的所有元素都不大于a[j]. 3.a[j+1]到a[hi]中的所有元素都不小于a[j]. 策略是先随意的取a[l

快速排序的几个变化形式

快速排序最简单的区间切分形式已经在前面的博文中介绍过了,是单向处理的,下面介绍快速排序的另几种形式,都是双向处理,即处理模式是,比Pivot小的往左移,比Pivot大的往右移,当两个方向相交后,把Pivot移动到相交位置往后的一个位置,最后形成三段,分别为比Pivot大,等于Pivot,大于Pivot.双向处理比单向处理的好处是,比Pivot大的元素一次移到右边后不需要再次移动,而单向处理里面,比Pivot大的元素则要不停的一次次往右移. 第一种:直接取最右边的元素为Pivot,此种切分方式代码

读书笔记-排序

[选择排序] a[i++] —> a[n],从前往后看.选择最小值.一次交换到位 1,完整循环找到数组中最小的元素: 2,把这个最小的元素与a[0]交换: 3,在a[i]-an的子数组中重复1-2步骤: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 for(int i = 0; i < n; i++) { int min = i; for(int j = i + 1; j < n; j++) { if(a[j] < min) min = j; } swap(i,

几种常见排序算法

几种常见排序算法 几种常见排序算法 写在前面 基础介绍 初级排序算法 selection sort选择排序 insertion sort插入排序 ShellSort希尔排序 shuffing不是排序算法 merge sort归并排序 Abstract in-place merge原地归并的抽象方法 Top-down mergesort自顶向下的归并排序 Bottom-up mergesort自底向上的归并排序 quicksort 三向切分的快速排序 Heapsort堆排序 总结和比较 命题 本文

如何优化合并排序和快速排序

和并排序和快速排序在元素的重复率特别高的时候排序的时间变长.我们可以利用三向切分的办法来避免相同的元素进行交换,以减少交换次数. 具体如下图所示: 总共有3个指针,lt,i,和gt,这个三个指针分别指着队首,队首的下一位,队尾.以队首为参考点,设该数组为a.设中间变量temp. temp ← a[lt] //队首设为参考变量 if a[i] < temp:           swap(a,lt++,i++) else if a[i] > temp:           swap(a,i,j-