浅析高速排序

浅析高速排序

By 钟桓

9月 16 2014 更新日期:9月 16 2014

文章文件夹

  1. 1. 基本思想与特性
  2. 2. 步骤与代码实现
  3. 3. 算法分析
    1. 3.1. (1)最坏时间复杂度
    2. 3.2. (2)
      最好时间复杂度
    3. 3.3. (3)基准keyword的选取
    4. 3.4. (4)平均时间复杂度
    5. 3.5. (5)空间复杂度
    6. 3.6. (6)稳定性
  4. 4. 參考资料:

基本思想与特性

高速排序是C.R.A.Hoare于1962年提出的一种划分交换排序。

它採用了一种分治的策略。通常称其为分治法(Divide-and-ConquerMethod)。

该方法的基本思想是:

  1. 先从数列中取出一个数作为基准数。
  2. 分区过程,将比这个数大的数全放到它的右边,小于或等于它的数全放到它的左边。
  3. 再对左右区间反复第二步,直到各区间仅仅有一个数。

高速排序算法的基本特性:

  1. 时间复杂度:O(n*lgn)
  2. 最坏:O(n^2)
  3. 空间复杂度:O(n*lgn)
  4. 不稳定。

高速排序是一种排序算法,对包括n个数的输入数组,平均时间为O(nlgn)。最坏情况是O(n^2)。

一般是用于排序的最佳选择。

由于,基于比較的排序,最快也仅仅能达到O(nlgn)。


步骤与代码实现

尽管高速排序称为分治法。但分治法这三个字显然无法非常好的概括高速排序的所有步骤。因此我的对高速排序作了进一步的说明:挖坑填数+分治法:

先来看实例吧,定义以下再给出(最好能用自己的话来总结定义,这样对实现代码会有帮助)。

以一个数组作为演示样例,取区间第一个数为基准数。

0 1 2 3 4 5 6 7 8 9
72 6 57 88 60 42 83 73 48 85

初始时,i = 0; j = 9; X = a[i] = 72

因为已经将a[0]中的数保存到X中,能够理解成在数组a[0]上挖了个坑,能够将其他数据填充到这来。

从j開始向前找一个比X小或等于X的数。当j=8,符合条件,将a[8]挖出再填到上一个坑a[0]中。a[0]=a[8]; i++; 这样一个坑a[0]就被搞定了,但又形成了一个新坑a[8],这怎么办了?简单,再找数字来填a[8]这个坑。这次从i開始向后找一个大于X的数。当i=3,符合条件。将a[3]挖出再填到上一个坑中a[8]=a[3]; j—;

0 1 2 3 4 5 6 7 8 9
48 6 57 88 60 42 83 73 48 85

i = 3; j = 7; X=72

再反复上面的步骤,先从后向前找,再从前向后找。

从j開始向前找,当j=5。符合条件,将a[5]挖出填到上一个坑中,a[3] = a[5]; i++;

从i開始向后找,当i=5时,因为i==j退出。

此时,i = j = 5。而a[5]刚好又是上次挖的坑,因此将X填入a[5]。

数组变为:

0 1 2 3 4 5 6 7 8 9
48 6 57 42 60 72 83 73 88 85

能够看出a[5]前面的数字都小于它,a[5]后面的数字都大于它。因此再对a[0…4]和a[6…9]这二个子区间反复上述步骤就能够了。

对挖坑填数进行总结

  1. i =L; j = R; 将基准数挖出形成第一个坑a[i]。
  2. j—由后向前找比它小的数,找到后挖出此数填前一个坑a[i]中。
  3. i++由前向后找比它大的数,找到后也挖出此数填到前一个坑a[j]中。
  4. 再反复运行2,3二步,直到i==j,将基准数填入a[i]中。

照着这个总结非常easy实现挖坑填数的代码:

void quick_sort1(int s[], int l, int r)
{
    if (l < r)
    {
        int i = AdjustArray(s, l, r);//先成挖坑填数法调整s[]
        quick_sort1(s, l, i - 1); // 递归调用
        quick_sort1(s, i + 1, r);
    }
}

//高速排序
void quick_sort(int s[], int l, int r)
{
    if (l < r)
    {
        //Swap(s[l], s[(l + r) / 2]); //将中间的这个数和第一个数交换 參见注1
        int i = l, j = r, x = s[l];
        while (i < j)
        {
            while(i < j && s[j] >= x) // 从右向左找第一个小于x的数
                j--;
            if(i < j)
                s[i++] = s[j];

            while(i < j && s[i] < x) // 从左向右找第一个大于等于x的数
                i++;
            if(i < j)
                s[j--] = s[i];
        }
        s[i] = x;
        quick_sort(s, l, i - 1); // 递归调用
        quick_sort(s, i + 1, r);
    }
}

算法分析

高速排序的时间主要耗费在划分操作上。对长度为k的区间进行划分。共需k-1次keyword的比較。

(1)最坏时间复杂度

最坏情况是每次划分选取的基准都是当前无序区中keyword最小(或最大)的记录,划分的结果是基准左边的子区间为空(或右边的子区间为空),而划分所得的还有一个非空的子区间中记录数目。只比划分前的无序区中记录个数降低一个。

 

因此,高速排序必须做n-1次划分,第i次划分開始时区间长度为n-i+1,所需的比較次数为n-i(1≤i≤n-1)。故总的比較次数达到最大值:

           Cmax = n(n-1)/2=O(n2)
 

假设按上面给出的划分算法,每次取当前无序区的第1个记录为基准。那么当文件的记录已按递增序(或递减序)排列时,每次划分所取的基准就是当前无序区中keyword最小(或最大)的记录。则高速排序所需的比較次数反而最多。

(2) 最好时间复杂度

 

在最好情况下,每次划分所取的基准都是当前无序区的”中值”记录。划分的结果是基准的左、右两个无序子区间的长度大致相等。总的keyword比較次数 :O(nlgn)

注意:  

    用递归树来分析最好情况下的比較次数更简单。由于每次划分后左、右子区间长度大致相等,故递归树的高度为O(lgn)。而递归树每一层上各结点所相应的划分过程中所须要的关键字比較次数总和不超过n。故整个排序过程所须要的关键字比較总次数C(n)=O(nlgn)。
    由于高速排序的记录移动次数不大于比較的次数,所以高速排序的最坏时间复杂度应为0(n2),最好时间复杂度为O(nlgn)。



(3)基准keyword的选取

在当前无序区中选取划分的基准keyword是决定算法性能的关键。

  • “三者取中”的规则

     “三者取中”规则。即在当前区间里,将该区间首、尾和中间位置上的keyword比較,取三者之中值所相应的记录作为基准。在划分開始前将该基准记录和该区伺的第1个记录进行交换。此后的划分过程与上面所给的Partition算法全然同样。  

  • 取位于low和high之间的随机数k(low≤k≤high)。用R[k]作为基准

     选取基准最好的方法是用一个随机函数产生一个取位于low和high之间的随机数k(low≤k≤high),用R[k]作为基准,这相当于强迫R[low..high]中的记录是随机分布的。

    用此方法所得到的高速排序一般称为随机的高速排序。详细算法【參见教材】

注意:  

随机化的高速排序与一般的高速排序算法区别非常小。

但随机化后。算法的性能大大地提高了,尤其是对初始有序的文件。一般不可能导致最坏情况的发生。

算法的随机化不只适用于高速排序,也适用于其他须要数据随机分布的算法。





(4)平均时间复杂度

虽然高速排序的最坏时间为O(n2),但就平均性能而言,它是基于keyword比較的内部排序算法中速度最快者,高速排序亦因此而得名。它的平均时间复杂度为O(nlgn)。

(5)空间复杂度

高速排序在系统内部须要一个栈来实现递归。

若每次划分较为均匀。则其递归树的高度为O(lgn),故递归后需栈空间为O(lgn)。最坏情况下,递归树的高度为O(n),所需的栈空间为O(n)。

(6)稳定性

高速排序是非稳定的.

时间: 2024-10-08 06:03:44

浅析高速排序的相关文章

高速排序算法C++实现

//quick sort //STL中也有现成的高速排序算法.内部实现採用了下面技巧 //1)枢轴的选择採取三数取中的方式 //2)后半段採取循环的方式实现 //3)高速排序与插入排序结合 #include<vector> #include<iostream> #include<algorithm> using namespace std; //这一版本号是最简单实现版本号.对于高速排序的优化主要有下面几个方面: //1)枢轴的选择.若枢轴选取不全适.比方,若每次递归时

白话经典算法系列之六 高速排序 高速搞定

高速排序因为排序效率在同为O(N*logN)的几种排序方法中效率较高,因此经常被採用,再加上高速排序思想----分治法也确实有用,因此非常多软件公司的笔试面试,包含像腾讯,微软等知名IT公司都喜欢考这个,还有大大小的程序方面的考试如软考,考研中也经常出现高速排序的身影. 总的说来,要直接默写出高速排序还是有一定难度的,因为本人就自己的理解对高速排序作了下白话解释,希望对大家理解有帮助,达到高速排序,高速搞定. 高速排序是C.R.A.Hoare于1962年提出的一种划分交换排序.它採用了一种分治的

排序算法之高速排序

这一个博客将高速排序讲的挺好的:http://blog.csdn.net/morewindows/article/details/6684558 一.算法思想描写叙述 1)令i = L,j = R,将基准数挖出形成第一个坑(基准数能够选择第一个位置上的数作为基准数,也能够选择其它位置上的数作为基准数). 2)j--   . 从后往前扫,找到第一个比基准数小的数.并将其"挖出",填到上一个形成的坑中arr[i] 3)i++ . 从前往后扫,找到第一个比基准数大的数.并将其"挖出

高速排序

所谓的高速排序,就是在一些中,找一个数(如果为a)为基准.把这些数分成两部分.一部分小于等于a,还有一部分大于a. 思路: (1)在这些数中,设置一个指针指向第一个(low),一个指针指向最后一个(high).设置一个基点(取第一个)为v (2)从高位置high開始.假设*high > v,则high--,然后看low,假设*low<v,则low++. (3)交换*low和*high (4)递归运行上面的 #include <stdio.h> void swap(int *a,in

希尔排序和高速排序

//希尔排序 在直接插入排序算法中,每次插入一个数,使有序序列仅仅添加1个节点,而且对插入下一个数没有提供不论什么帮助. 假设比較相隔较远距离(称为增量)的数,使得数移动时能跨过多个元素,则进行一次比較就可能消除多个元素交换. D.L.shell于1959年在以他名字命名的排序算法中实现了这一思想.算法先将要排序的一组数按某个增量d分成若干组, 每组中记录的下标相差d.对每组中所有元素进行排序,然后再用一个较小的增量对它进行,在每组中再进行排序. 当增量减到1时,整个要排序的数被分成一组,排序完

高速排序算法

高速排序算法 作者 July  二零一一年一月四日------------------------------------------ 写之前,先说点题外话.每写一篇文章,我都会遵循下面几点原则:一.保持版面的尽量清晰,力保排版良好.二.力争所写的东西,清晰易懂,图文并茂三.尽最大可能确保所写的东西精准,有实用价值. 由于,我认为,你既然要把你的文章,发布出来,那么你就一定要为你的读者负责.不然,就不要发表出来.一切,为读者服务. ok,闲不多说.接下来,咱们立马进入本文章的主题,排序算法.众所

经典排序之高速排序

高速排序堪称最经典的排序.思想是选一个基准值.一般选第一个.然后全部的值与基准值比較,大的位置不变, 小的位置与第一个大的位置互换,就这样进行第一轮排序.然后对于基准值二边的再进行排序,依次类推,直到为剩一个. 以下是快排的代码: public class QuickSort { public void quickSort(int a[],int start,int end){ //比較这个数组是否仅仅有一个值没有进行高速排序 if(start<end){ //基准值 int keyword=a

《算法导论》 — Chapter 7 高速排序

序 高速排序(QuickSort)也是一种排序算法,对包括n个数组的输入数组.最坏情况执行时间为O(n^2). 尽管这个最坏情况执行时间比較差.可是高速排序一般是用于排序的最佳有用选择.这是由于其平均性能相当好.期望的执行时间为O(nlgn).且O(nlgn)中隐含的常数因子非常小.另外它还能够进行就地排序在虚拟环境中也能非常好的工作. GitHub chapter 7 程序代码下载 原理 高速排序也和合并排序一样,基于分治法,分为分解.解决.合并三个步骤. 分解:数组array[low-hig

交换排序之高速排序

今天大鹏哥跟大家一起学习下交换排序中的高速排序. 高速排序是对冒泡排序的一种改进.它的基本思想是.通过一趟排序将待排记录切割成独立的两部分,当中一部分记录的keyword均比还有一部分的keyword小.则可分别对这两部分记录继续进行排序,以达到真个序列有序. 高速排序基本步骤: Step1.定义两个变量low和high,他们的初值分别为low和high,此外另一个变量pivotkey. Step2.首先从high所指位置向前搜索找到第一个keyword小于pivotkey的记录和pivotke