算法导论 - 快速排序的 C 语言实现

在实践中,需要对大量数据排序时,快速排序常常是最佳的选择。快速排序是基于分治思想的一种算法,和归并排序有相似之处。在最坏情况下,快速排序的时间复杂度是O( n^2 );平均情况下,快速排序的时间复杂度是 O( n * lg n)。我们知道归并排序的时间复杂度是O( n * lg n),那么为什么在实践中会发现快速排序常常要优于归并排序呢?通过分析快速算法,我们可以发现最坏情况出现的概率极为小;而在平均情况下,由于快速排序省去了分治思想中的合并过程,从而节省了时间。这是它能够优于归并排序的主要原因。另外,快速排序不需要额外的空间,具有空间复杂度优势。

1. 算法导论中给出的快速排序算法的主要思想是

(1) 假设要对数组 A[ p : r] 以升序排列,我们可以待排序数组中最后一个元素A[r] 作为基准数。

(2) 借助一个标记 i,使它初始状态下,指向数组的第一个元素之前,i 左边没有元素,我们认为此时 i 左边的元素都比基准数小。在之后的过程中,保证 i 左边的元素都不大于基准数,也是使算法正确的必要条件。

(3) 利用指针 j 从第一个元素开始扫描数组,遇到小于或等于基准数的元素时,就将 i加 1,然后交换该元素与此时 i 指向的元素,这样就保证了 i 左边的元素都不大于基准数。遇到大于基准数的元素就保持 i 指向不变,继续扫描,这样同时保证了 i 右边 j 左边的的元素都比基准数大。扫描完基准数前一个元素时,终止扫描,并将此时 i+1 指向的元素与 A[r] 互换。这样之后,我们就发现A[i+1] 左边的元素都不大于 A[i+1],而 A[i+1] 右边的元素都不小于 A[i+1],从而以A[i+1] 为中心点把 A[ p : r] 分成了两部分。

(4) 分别对上一步划分出的两部分(A[ p : i])和(A[i+2 : r])重复上述三步操作(递归),直到待排序子数组的起始下标小于等于终止下标。全部完成后,就得到了排好序的新数组。

下图表现了最开始的一次递归过程。


C 语言实现快速排序算法的代码如下。

int partition(int *src, int st, int ed){
        int i = st - 1;
        int j = st;
        int key = src[ed];
        int tmp = 0;
        for(; j < ed; ++j){
                if(src[j] <= key){
                        ++i;
                        tmp = src[i];
                        src[i] = src[j];
                        src[j] = tmp;
                }
        }
        tmp = src[i+1];
        src[i+1] = key;
        src[ed]  = tmp;
        return i+1;
}

void quickSort(int *src, int st, int ed){
        if(st<ed){
                int md = partition(src, st, ed);
                quickSort(src, st, md-1);
                quickSort(src, md+1, ed);
        }
}

2. 上面的例子是快速排序算法的一种平凡(trivial solution)实现,实际上,C.RA. Hoare 博士在1962年首次提出快速排序算法时,给出了非平凡的(nontrivial solution)实现。相比算法导论中给出的解法,它是更优的,这主要是因为每趟遍历过程中,它只扫描一个元素一次。理解该实现方法的要点在于由于选取中心点并将它复制给变量key 后,在待排序数组中就产生了一个空位。在之后的遍历过程中,每次向空位赋值后又产生了新的一个空位,这样可以一直利用将合适的值放入空位,然后产生新的空位,直到遍历结束。

C 语言实现该算法思想的代码如下。

void quickSort(int *src, int st, int ed){
        if(st >= ed) return; 
        int key = src[st];
        int i = st, j = ed;
        while(i < j){
                for(; j > i; --j){
                        if(src[j] < key){
                                src[i] = src[j];
                                ++i;
                                break;
                        }
                }
                for(; i < j; ++i){
                        if(src[i] > key){
                                src[j] = src[i];
                                --j;
                                break;
                        }
                }
        }
        src[i] = key;
        
        quickSort(src, st, i-1);
        quickSort(src, i+1, ed);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值