快速排序

来源:互联网 发布:围棋点目软件 编辑:程序博客网 时间:2024/06/07 01:08

快速排序


先看个例子:
对于数组 a = [427 353 112 914 141 555 96 696 294 883],我们选择tmp = a[0]作为我们的枢纽元,在实现第一轮排序后,我们要实现的效果是数组a分成两部分,前面的部分元素全部小于等于**tmp,后面部分全部大于等于**tmp。取i = 0; j = a.length-1; tmp = a[0]

第一轮排序过程如下
1. 从j开始,我们找到第一个小于 tmp=427 的元素,为 a[8]=294,将 a[j] 值存储到 i 位置,此时数组为
a = [294 353 112 914 141 555 96 696 294 883]。同时,i++ (因为当前元素已经确保小于等于 tmp)
2. 从 i 开始,我们找到第一个大于 tmp=427 的元素,为 a[3] = 914,将 a[i] 值存储到 j 位置,此时数组为 a = [294 353 112 914 141 555 96 696 914 883] ,同时 j–
3. 循环步骤1和步骤2,直到 i >= j。最后,将 tmp 值赋予 a[i]
4. 经过第一趟排序后,我们数组的值为 a = [294 353 112 96 141 294 555 696 914 883]。此时我们发现,a[0..5]的值小于等于tmp,a[6..9]的值大于等于 tmp

在第一轮排序后我们得到两个数组,分别为 S1 = a[0..5], S2 = a[6..9],这时我们再对这两个数组各自执行上述步骤,不断递归下去,则最终可以得到一个排序数组。

上述步骤可总结为:

  • 如果当前数组元素个数小于等于1,直接返回
  • 选择数组中任一元素,称为枢纽元,这里为了方便我们选择i = 0为枢纽元,值设为 tmp
  • 设数组左端点为 i,右端点为 j
  • 在每轮排序中,我们将当前数组分为两个数组 S1,S2。其中 S1 数组的所有值均小于等于 tmp,S2 的所有元素均大于等于 tmp
  • 执行 quickSort(S1), quickSort(S2)

以第一个元素为枢纽元的实现如下

void quickSort(int[] a, int left, int right) {     if (left >= right)         return;     int x = a[left];     int i = left;     int j = right;     while (i  < j) {         while (i < j && a[j] > x) j--;         if (i < j) a[i++] = a[j];         while (i < j && a[i] < x) i++;         if (i < j) a[j--] = a[i];     }     a[i] = x; // 此处注意将a[i]替换回枢纽元元素     quickSort(a, i+1, right);     quickSort(a, left, i-1); }

其平均运行时间为O(N log N)。最坏运行时间为O(N^2)

上面为了理解简单,我们的枢纽元直接选择了数组的第一个元素,当数组是随机的时候,则不存在问题,因为现在所有元素都是等价的,但如果该数组是预排序或者反序的,则我们可发现,所有元素只能被划入集合S1或集合S2。假设数组是预排序的,且 a[0] != [a1](相等则是另一种类似情况),则第一轮排序后 S1=a[0], S2=a[1...n],则此时 O(n^2) ;同理,当数组是逆序时,同样有 O(N^2)。故直接使用数组的第一个元素是不好的选择。
通常情况下,随机选择数组任意一位作为枢纽元素是安全的,这里我们选择数组的中间位置的元素,代码如下

void Quick(int *a, int left, int right){    if (left >= right) return;    int prvot = (left+right) / 2;    int tmp = a[prvot];    int i = left;    int j = right;    while (i < j) {        while (i < j && a[j] > tmp) j--;        if (i < j) {            a[prvot] = a[j];            prvot = j;        }        while (i < j && a[i] < tmp) i++;        if (i < j) {            a[prvot] = a[i];            prvot = i;            j--; // 这里注意,否则出现重复值时可能进入死循环        }    }    a[prvot] = tmp;    Quick(a, prvot+1, right);    Quick(a, left, prvot-1);}

完整代码参考: quickSort.cpp

0 0
原创粉丝点击