QuickSort快速排序的多种实现

并不是很懂wikipedia上面说快排的空间复杂度最坏情况是O(NlogN)啊,难道不是空间复杂度平均O(logN),最坏O(N)么……原地快排难道不是只要算递归栈深度就好了么……有谁给我解释一下啊(评论区orz)。

快排的核心思想我感觉就是选一个pivot,然后将待排序数列通过与pivot比较分为两个部分A和B(A部分的数均小于或等于pivot,B部分均大于或等于pivot),这样划分好的数列中的数就处于A <= pivot <= B的状态,接着对划分好的两个部分A和B分别递归调用快排函数,最后一定能出现划分好的部分中只剩下一个元素或没有元素的情况,而对于只有一个元素的数列或空列,我们认为它已经是有序的,这样就把数列排好了。

所以在快排中怎样对数组进行划分是关键,不同版本的快速排序大体结构都差不多,不同之处就在于划分函数的不同,这里就暂时称它为Partition函数吧,下面介绍四种不同的实现。

第一个版本

其实快排一开始不是我们现在看到的这样的,一开始Hoare发明的版本在《算法导论》这本书的思考题7-1上有提及。这个版本相当于是以开头元素为pivot,然后两个index分别从头和尾开始遍历,暂时把这两个index变量称为i和j吧,i从头开始,j从尾开始,其过程如下:

第一步——j往前遍历,遇到小于或等于pivot的数就停止

第二步——i往后遍历,遇到大于或等于pivot的数就停止

第三步——比较i和j大小,j > i则交换i和j位置上的数并在此基础上继续第一步,否则函数返回j

函数返回后,经过Partition函数的调用,数组中下标为j之前(包括j)的元素都小于或等于pivot,j之后的元素都大于或等于pivot,然后对这两部分进行快速排序。

例子:

代码如下:

int Hoare_Partition(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin - 1;;
    int j = end + 1;
    int tmp;

    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        do{
            i++;
        }while(A[i] < pivot);
        if(j > i){
            tmp = A[i];
            A[i] = A[j];
            A[j] = tmp;
        }
        else{
            return j;
        }
    }
}

void Quick_Sort_1(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = Hoare_Partition(A, begin, end);
    Quick_Sort_1(A, begin, pivot_position);
    Quick_Sort_1(A, pivot_position + 1, end);
}

第二个版本

这个版本是对Hoare原版本的一个改进,Hoare原版本中Partition函数调用结束后pivot会存在于划分好的两个部分中的某一个中,因此第二个版本对此进行了改进,同样选取第一个元素作为pivot,如果数列的下标从begin开始,end结束的话,那么用Hoare的处理思想去处理从begin + 1到end部分的数列(Hoare的版本是处理从begin到end部分的数列的),这样处理完成后下标为begin + 1到j的数都是小于或等于pivot的,而下标为j + 1到end的数都是大于或等于pivot的,最后再将pivot和下标为j的数进行交换并返回j,这样一来下标为j之前(不包括j)的数均小于或等于pivot,下标为j之后的数均大于或等于pivot,pivot也就不在两个划分好的部分中,而独立存在了,因此只要对两个部分继续快排就好了,而不需要再将pivot包括进去。

例子:

代码如下:

int New_Partition_1(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin;
    int j = end + 1;
    int tmp;

    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        do{
            i++;
        }while(A[i] < pivot);
        if(j > i){
            tmp = A[i];
            A[i] = A[j];
            A[j] = tmp;
        }
        else{
            A[begin] = A[j];
            A[j] = pivot;
            return j;
        }
    }
}

void Quick_Sort_2(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_1(A, begin, end);
    Quick_Sort_2(A, begin, pivot_position - 1);
    Quick_Sort_2(A, pivot_position + 1, end);
}

 第三个版本

这个版本是现在比较常见的版本之一,将pivot所在位置挖成一个坑,i从begin + 1开始,j从end开始,然后:

第一步——j往前遍历,找到小于或等于pivot的数

     如果此时j > i,将下标为j的数填入现在的坑中,并在下标为j的位置产生一个新坑,进行第二步

     否则就将pivot填入现在的坑中,并返回j

第二步——i往后遍历,找到大于或等于pivot的数

     如果此时j > i,将下标为i的数填入现在的坑中,并在下标为i的位置产生一个新坑,进行第一步

     否则就将pivot填入现在的坑中,并返回i

例子:

代码如下:

int New_Partition_3(int A[ ], int begin, int end){
    int pivot = A[end];
    int i = begin, j = begin;
    int tmp;

    for(i = begin; i < end; i++){
        if(A[i] < pivot){
            tmp = A[j];
            A[j] = A[i];
            A[i] = tmp;
            j++;
        }
    }
    A[end] = A[j];
    A[j] = pivot;
    return j;
}

void Quick_Sort_3(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_2(A, begin, end);
    Quick_Sort_3(A, begin, pivot_position - 1);
    Quick_Sort_3(A, pivot_position + 1, end);
}

第四个版本

这个版本也很常见,就是把最后一个数作为pivot,i一开始等于begin,用j从begin开始往end - 1遍历,找到小于或等于pivot的数,与i所在位置的数交换,并将i++,最后将pivot和下标为i的数交换,返回i。

例子:

代码如下:

int New_Partition_3(int A[ ], int begin, int end){
    int pivot = A[end];
    int i = begin, j = begin;
    int tmp;

    for(i = begin; i < end; i++){
        if(A[i] < pivot){
            tmp = A[j];
            A[j] = A[i];
            A[i] = tmp;
            j++;
        }
    }
    A[end] = A[j];
    A[j] = pivot;
    return j;
}

void Quick_Sort_4(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_3(A, begin, end);
    Quick_Sort_4(A, begin, pivot_position - 1);
    Quick_Sort_4(A, pivot_position + 1, end);
}

完整代码:

//
//  main.c
//  Quick Sort
//
//  Created by 余南龙 on 2016/11/29.
//  Copyright ? 2016年 余南龙. All rights reserved.
//

#include <stdio.h>

int Hoare_Partition(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin - 1;;
    int j = end + 1;
    int tmp;

    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        do{
            i++;
        }while(A[i] < pivot);
        if(j > i){
            tmp = A[i];
            A[i] = A[j];
            A[j] = tmp;
        }
        else{
            return j;
        }
    }
}

int New_Partition_1(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin;
    int j = end + 1;
    int tmp;

    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        do{
            i++;
        }while(A[i] < pivot);
        if(j > i){
            tmp = A[i];
            A[i] = A[j];
            A[j] = tmp;
        }
        else{
            A[begin] = A[j];
            A[j] = pivot;
            return j;
        }
    }
}

int New_Partition_2(int A[ ], int begin, int end){
    int pivot = A[begin];
    int i = begin;
    int j = end + 1;

    while(1){
        do{
            j--;
        }while(A[j] > pivot);
        if(j > i){
            A[i] = A[j];
        }
        else{
            A[i] = pivot;
            return i;
        }
        do{
            i++;
        }while(A[i] < pivot&&i < j);
        if(j > i){
            A[j] = A[i];
        }
        else{
            A[j] = pivot;
            return j;
        }
    }
}

int New_Partition_3(int A[ ], int begin, int end){
    int pivot = A[end];
    int i = begin, j = begin;
    int tmp;

    for(i = begin; i < end; i++){
        if(A[i] < pivot){
            tmp = A[j];
            A[j] = A[i];
            A[i] = tmp;
            j++;
        }
    }
    A[end] = A[j];
    A[j] = pivot;
    return j;
}

void Quick_Sort_1(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = Hoare_Partition(A, begin, end);
    Quick_Sort_1(A, begin, pivot_position);
    Quick_Sort_1(A, pivot_position + 1, end);
}

void Quick_Sort_2(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_1(A, begin, end);
    Quick_Sort_2(A, begin, pivot_position - 1);
    Quick_Sort_2(A, pivot_position + 1, end);
}

void Quick_Sort_3(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_2(A, begin, end);
    Quick_Sort_3(A, begin, pivot_position - 1);
    Quick_Sort_3(A, pivot_position + 1, end);
}

void Quick_Sort_4(int A[ ], int begin, int end){
    int pivot_position;
    if(begin >= end)
        return;
    pivot_position = New_Partition_3(A, begin, end);
    Quick_Sort_4(A, begin, pivot_position - 1);
    Quick_Sort_4(A, pivot_position + 1, end);
}

int main(int argc, const char * argv[]) {
    int A_1[10] = {2, 4, 6, 1, 5, 9, 0, 8, 7, 3};
    int A_2[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    int A_3[10] = {9, 8, 7, 6, 5, 4, 3, 2, 1, 0};
    int A_4[10] = {3, 9, 1, 7, 0, 2, 8, 5, 4, 6};
    int i;

    Quick_Sort_1(A_1, 0, 9);
    for(i = 0; i < 10; i++){
        printf("%d ", A_1[i]);
    }
    putchar(‘\n‘);
    Quick_Sort_2(A_2, 0, 9);
    for(i = 0; i < 10; i++){
        printf("%d ", A_2[i]);
    }
    putchar(‘\n‘);
    Quick_Sort_3(A_3, 0, 9);
    for(i = 0; i < 10; i++){
        printf("%d ", A_3[i]);
    }
    putchar(‘\n‘);
    Quick_Sort_4(A_4, 0, 9);
    for(i = 0; i < 10; i++){
        printf("%d ", A_4[i]);
    }
    putchar(‘\n‘);
}
时间: 2024-11-06 17:11:15

QuickSort快速排序的多种实现的相关文章

快速排序的多种思路实现

快速排序的多种思路实现: 两边想中间靠拢: //  两边想中间靠拢,当a[left]<key a[right]>key时,两者交换 int PartSortBothSize(int *a, int left, int right) {  assert(a != NULL);  int key = a[right];  int begin = left;  int end = right - 1;  while (begin < end)  {   while (begin<end 

算法分析之——quick-sort快速排序

快速排序是一种排序算法,最坏情况运行时间为θ(n2),但其最佳期望运行时间为θ(nlgn),并且θ(nlgn)记号中隐含的常数因子很小,快排是在就地排序的一种排序算法.快排是基于分治思想的,与归并排序一样.快速排序是一种不稳定的排序算法,因为算法实现过程中涉及到元素交换. 思路: (1)分解:数组A[n]被划分两个字数组A[0..q-1]和A[q+1..n],使得对于数组A[0..q-1]中的元素都小于A[q], A[q+1..n]中的元素都大于等于A[q].此时A[q]就得排好序. (2)解决

[硕.Love Python] QuickSort(快速排序)

def partition(a, i, j):     k = -1     while True:         while k * (a[i] - a[j]) >= 0:             if i == j:                  return i             j += k         a[i], a[j] = a[j], a[i]         i, j, k = j, i - k, -k def qsort(a, i, j):     if i <

quicksort 快速排序

javascript function qsort(a, comp) { if (typeof comp==="undefined") { comp = function(a, b) { return a - b < 0; } } function _qsort(a, low, high) { if (!comp(low, high)) { return; } var first = low, last = high, pivot = a[first]; while (comp(

希尔排序和快速排序

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

java基础算法之快速排序

快速排序(Quicksort)是对冒泡排序的一种改进.在大学学过之后现在基本忘了,最近在好多地方都看到说快速排序在面试会问到,于是自己也准备重新拾起以前忘记的东西来,慢慢的积累自己的基础知识.fighting 算法概念 快速排序由C. A. R. Hoare在1962(50多年了呢)年提出,它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有

快速排序示例-java

package Quicksort; /** * 快速排序 * @author Administrator * */public class TestMain { /** * * @param start 开始位置 * @param end 结束位置 */ public static void sortArray(int[] array,int start,int end) { if(start >= end) { return ; } int startIndex = start; int e

快速排序算法2---以第一个元素作为主元

前面写过一个关于快速排序的算法,那个写得很仔细了,对于理解快排的思想是足够,也很好的.但近期在做笔试的题目碰到的关于快速的排序的题目,发现一般题目都是指定数组的第一个元素做主元,而我却比较擅长把最后一个元素作为主元,搞得很尴尬.其实要想达到快排最好的时间复杂度,除了待排序的数组是无序外,主元的选择也是一个决定因素,选择第一个和最后一个元素作为主元,其实都不是最好的选择.只有在随机挑选主元才是有可能达到最好的时间复杂度的. 假设要排序是A[0]……A[N-1],首先选用数组的第一个数作为关键数据,

数据结构-快速排序算法

一趟快速排序的算法是: 1)设置两个变量i.j,排序开始的时候:i=0,j=N-1: 2)以第一个数组元素作为关键数据,赋值给key,即key=A[0]: 3)从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换: 4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换: 5)重复第3.4步,直到i=j: (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于ke