快速排序

来源:互联网 发布:淘宝代理商怎么做 编辑:程序博客网 时间:2024/06/03 16:02

快速排序演示链接


排序思想

        通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,再分别对这两部分记录进行下一趟排序,以达到整个序列有序。

排序过程

设待排序的记录序列是R[s…t] ,在记录序列中任取一个记录(一般取R[s])作为参照(又称为基准或枢轴),以R[s].key为基准重新排列其余的所有记录,方法是:

      ◆所有关键字比基准小的放R[s]之前;
      ◆ 所有关键字比基准大的放R[s]之后。
       以R[s].key最后所在位置i作为分界,将序列R[s…t]分割成两个子序列,称为一趟快速排序。

(1)一趟快速排序方法

排序思想

        从序列的两端交替扫描各个记录,将关键字小于基准关键字的记录依次放置到序列的前边;而将关键字大于基准关键字的记录从序列的最后端起,依次放置到序列的后边,直到扫描完所有的记录。
        设置指针low,high,初值为第1个和最后一个记录的位置。

设两个变量i,j,初始时令i=low,j=high,以R[low].key作为基准(将R[low]保存在tmp中) 。
    ① 从j所指位置向前搜索:将tmp.key与R[j].key进行比较:
        ◆  若tmp.key≤R[j].key :令j=j-1,然后继续进行比较, 直到i=j或tmp.key>R[j].key为止;
        ◆ 若tmp.key>R[j].key :R[i]=R[j],腾空R[j]的位置, 且令i=i+1;
    ② 从i所指位置起向后搜索:将tmp.key与R[i].key进行比较:
        ◆ 若tmp.key≥R[i].key :令i=i+1,然后继续进行比较, 直到i=j或tmp.key<R[i].key为止;

        ◆ 若tmp.key<R[i].key :R[j]=R[i],腾空R[i]的位置, 且令j=j-1;
重复①、②,直至i=j为止,i就是tmp(基准)所应放置的位置。

一趟排序示例

设有6个待排序的记录,关键字分别为29, 38, 22, 45, 23, 67,一趟快速排序的过程如图1所示。


算法实现

int quick_one_pass(int value[], int low, int high){      int i=low, j=high;    tmp = value[i];       /*   tmp作为临时单元和哨兵  */    do    {        while(tmp<=value[j] && j>i)            j--;        if(j>i)        {             value[i] = value[j];             i++;        }        while(tmp>=value[i] && j>i)            i++;        if(j>i)        {            value[j] = value[i];            j--;        }    }while(i!=j);    /*   i=j时退出扫描  */    value[i] = tmp;    return(i);}

(2)快速排序算法实现

当进行一趟快速排序后,采用同样方法分别对两个子序列快速排序,直到子序列记录个为1为止。

① 递归算法

void quick_Sort(int value[], int low, int high){    int k;    if(low<high)                /*   序列分为两部分后分别对每个子序列排序   */    {        k = quick_one_pass(value, low, high);        quick_Sort(value, low, k-1);        quick_Sort(value, k+1, high);    }}

②  非递归算法

#define  MAX_STACK  100void quick_Sort(int value[], int low, int high){    int k, stack[MAX_STACK], top=0;    do    {        while(low<high)        {            k=quick_one_pass(value, low, high);            stack[++top]=high;            stack[++top]=k+1;            /*  第二个子序列的上,下界分别入栈  */            high=k-1;        }        if(top!=0)        {            low=stack[top--];            high=stack[top--];        }    }while(top!=0 && low<high);}

算法分析

快速排序的主要时间是花费在划分上,对长度为k的记录序列进行划分时关键字的比较次数是k-1 。设长度为n的记录序列进行排序的比较次数为C(n),则C(n)=n-1+C(k)+C(n-k-1) 。
◆  最好情况:每次划分得到的子序列大致相等,则
C(n)≤n+2×C(n/2)+C(n-k-1)
≤n+2×[n/2+ 2×C((n/2)/2)≤ 2n+4×C(n/4)
≤…
≤h×n+2h×C(n/2h) ,当n/2h=1时排序结束。

即C(n)≤n×㏒2n+n×C(1) ,C(1)看成常数因子,
即C(n)≤O(n×㏒2n) ;
◆  最坏情况:每次划分得到的子序列中有一个为空,另一个子序列的长度为n-1。即每次划分所选择的基准是当前待排序序列中的最小(或最大)关键字。

比较次数:即C(n)=O(n2)

◆  一般情况: 对n个记录进行快速排序所需的时间T(n)组成是:
     ① 对n个记录进行一趟划分所需的时间是:n×C ,C是常数;
     ② 对所得到的两个子序列进行快速排序的时间:
Tavg(n)=C(n)+Tavg(k-1)+Tavg(n-k)          ……  ⑴

若记录是随机排列的,k取值在1~n之间的概率相同,则:

     

只有1个记录的排序时间是一个常数,
∴ 快速排序的平均时间复杂度是:T(n)=O(n㏒2n)
从所需要的附加空间来看,快速排序算法是递归调用,系统内用堆栈保存递归参数,当每次划分比较均匀时,栈的最大深度为[㏒2n]+1 。
∴ 快速排序的空间复杂度是:S(n)=O(㏒2n)
从排序的稳定性来看,快速排序是不稳定的。

原创粉丝点击