• 常见排序算法导读(6)[快排序]


    上一节介绍了最简单的交换排序 - 冒泡排序,这一节将介绍平均性能比冒泡排序要好很多的一种交换排序,那就是快排序(Quick Sort)。

    快排序的基本思想

    快排序(Quick Sort)是一种分治的排序算法。

    • 首先从待排序对象中选出一个基准对象(Pivot), 通常取第一个对象;
    • 然后调整所有待排序对象,将基准对象放置到某个特定的位置,使基准对象左边的所有对象的关键字都小于等于基准对象的关键字,使基准对象右边的所有对象的关键字都大于等于基准对象的关键字;
    • 最后把基准对象左边的那些对象与基准对象右边的那些对象分别看做两个独立的序列,再对这两个序列分别(递归调用快排序算法)排序。

    典型的快排序过程是这样子滴,图片来源戳这里

    快速排序之所以比较快,是因为相对于冒泡排序来说,每次交换都是跳跃式的。每次排序的时候设置一个基准对象(Pivot),将关键字小于等于基准对象的关键字的所有对象都放置到基准对象的左边,将关键字大于等于基准对象的关键字的所有对象都放置到基准对象的右边。这样在每次交换的时候就不会像冒泡排序一样只能在相邻的对象之间进行交换,交换的距离就大得多了。因此,总的比较和交换次数就减少了,速度自然也就提高了。

    o 快排序(Quick Sort)的C代码实现

     1 /*
     2  * Basic Idea of QuickSort
     3  *
     4  * 1. Pick an element in the array as the pivot element
     5  *
     6  * 2. Make a pass to the array, called the PARTITION step, which rearranges
     7  *    the elements in the array:
     8  *    a. The pivot element is in the proper place
     9  *    b. The elements less    than pivot element are on the left  of it
    10  *    c. The elements greater than pivot element are on the right of it
    11  *
    12  * 3. Recursively apply the above process to the left and right part of
    13  *    the pivot element
    14  */
    15 
    16 #define QSORT(a, n)     quicksort(a, 0, n - 1)
    17 
    18 void quicksort(int a[], int left, int right)
    19 {
    20         if (right <= left)
    21                 return;
    22 
    23         int j = partition(a, left, right); // PARTITION
    24         quicksort(a, left,  j - 1); // sort left  part a[left .. j-1]
    25         quicksort(a, j + 1, right); // sort right part a[j+1  .. right]
    26 }
    27 
    28 static int partition(int a[], int left, int right)
    29 {
    30         // left and right scan indices
    31         int i = left + 1;
    32         int j = right;
    33 
    34         // get partitioning item i.e. pivot
    35         int pivot = a[left];
    36 
    37         // scan right, scan left, check for scan complete, and exchange
    38         while (1) {
    39                 for (; j > left; j--) { // scan right
    40                         if (a[j] < pivot)
    41                                 break;
    42                 }
    43 
    44                 for (; i < right; i++) { // scan left (i<=right also works but unnecessary)
    45                         if (a[i] > pivot)
    46                                 break;
    47                 }
    48 
    49                 if (i >= j)
    50                         break;
    51 
    52                 exchange(a, i, j);
    53         }
    54 
    55         //
    56         // put pivot = a[j] into the right position, with
    57         //      a[left .. j-1] <= a[j] <= a[j+1 .. right]
    58         //
    59         exchange(a, left, j);
    60 
    61         return j;
    62 }
    63 
    64 static void exchange(int a[], int i, int j)
    65 {
    66         int t = a[i];
    67         a[i] = a[j];
    68         a[j] = t;
    69 }

    接下来,给出一个完整的qsort.c并编译后测试, 以便形象化地理解冒泡排序的全过程。
    o qsort.c 注意:下面的程序中某些代码行风格很不好是故意的,因为只是为了帮助打印排序过程故一切从简。

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 
      4 typedef struct obs_s {
      5         unsigned int loop;
      6         unsigned int swap;
      7 } obs_t;
      8 
      9 obs_t g_obs = { .loop = 0, .swap = 0 };
     10 
     11 size_t n = 0; /* size of array */
     12 
     13 static void show(int a[], size_t n);
     14 static void exchange(int a[], int i, int j);
     15 static int partition(int a[], int left, int right);
     16 
     17 #define QSORT(a, n)     quicksort(a, 0, n - 1)
     18 
     19 void quicksort(int a[], int left, int right)
     20 {
     21         if (right <= left)
     22                 return;
     23 
     24         int j = partition(a, left, right); // PARTITION
     25         quicksort(a, left,  j - 1); // sort left  part a[left .. j-1]
     26         quicksort(a, j + 1, right); // sort right part a[j+1  .. right]
     27 }
     28 
     29 static int partition(int a[], int left, int right)
     30 {
     31         // left and right scan indices
     32         int i = left + 1;
     33         int j = right;
     34 
     35         // get partitioning item i.e. pivot
     36         int pivot = a[left];
     37 
     38         // scan right, scan left, check for scan complete, and exchange
     39         while (1) {
     40                 for (; j > left; j--) { g_obs.loop++; // scan right
     41                         if (a[j] < pivot)
     42                                 break;
     43                 }
     44 
     45                 for (; i < right; i++) { g_obs.loop++; // scan left
     46                         if (a[i] > pivot)
     47                                 break;
     48                 }
     49 
     50                 if (i >= j)
     51                         break;
     52 
     53                 g_obs.swap++;
     54                 printf("#%d:%d		", left, right); show(a, n);
     55                 printf("	<-- swap(a[%d], a[%d])
    ", i, j);
     56 
     57                 exchange(a, i, j);
     58         }
     59 
     60         //
     61         // put pivot = a[j] into the right position, with
     62         //      a[left .. j-1] <= a[j] <= a[j+1 .. right]
     63         //
     64         g_obs.swap++;
     65         printf("#%d:%d		", left, right); show(a, n);
     66         printf("	<-- swap(a[%d], a[%d])	 next pivot : a[%d]
    ", left, j, j);
     67 
     68         exchange(a, left, j);
     69 
     70         return j;
     71 }
     72 
     73 static void exchange(int a[], int i, int j)
     74 {
     75         int t = a[i];
     76         a[i] = a[j];
     77         a[j] = t;
     78 }
     79 
     80 static void show(int a[], size_t n)
     81 {
     82         for (int i = 0; i < n; i++)
     83                 printf("%c  ", a[i]);
     84 }
     85 
     86 int main(int argc, char *argv[])
     87 {
     88         if (argc < 2) {
     89                 fprintf(stderr, "Usage: %s <C1> [C2] ...
    ", argv[0]);
     90                 return -1;
     91         }
     92 
     93         argc--;
     94         argv++;
     95 
     96         n = argc;
     97         int *a = (int *)malloc(sizeof(int) * n);
     98 #define VALIDATE(p) do { if (p == NULL) return -1; } while (0)
     99         VALIDATE(a);
    100 
    101         for (int i = 0; i < n; i++)
    102                 *(a+i) = argv[i][0];
    103 
    104         printf("                ");
    105         for (int i = 0; i < n; i++)
    106                 printf("%-2d ", i);
    107         printf("
    ");
    108 
    109         printf("Before sorting: "); show(a, n); printf("
    ");
    110         QSORT(a, n);
    111         printf("After  sorting: "); show(a, n); printf("
    ");
    112 
    113         printf("
    ");
    114         printf("Total loop times : %2d
    ", g_obs.loop);
    115         printf("Total swap times : %2d
    ", g_obs.swap);
    116 
    117         free(a); a = NULL;
    118         return 0;
    119 }

    o 编译并测试

    $  gcc -g -Wall -m32 -std=c99 -o qsort qsort.c
    
    $ ./qsort       K  R  A  T  E  L  E  P  U  I  M  Q  C  X  O  S
                    0  1  2  3  4  5  6  7  8  9  10 11 12 13 14 15 
    Before sorting: K  R  A  T  E  L  E  P  U  I  M  Q  C  X  O  S  
    #0:15           K  R  A  T  E  L  E  P  U  I  M  Q  C  X  O  S  <-- swap(a[1], a[12])
    #0:15           K  C  A  T  E  L  E  P  U  I  M  Q  R  X  O  S  <-- swap(a[3], a[9])
    #0:15           K  C  A  I  E  L  E  P  U  T  M  Q  R  X  O  S  <-- swap(a[5], a[6])
    #0:15           K  C  A  I  E  E  L  P  U  T  M  Q  R  X  O  S  <-- swap(a[0], a[5])   next pivot : a[5]
    #0:4            E  C  A  I  E  K  L  P  U  T  M  Q  R  X  O  S  <-- swap(a[0], a[2])   next pivot : a[2]
    #0:1            A  C  E  I  E  K  L  P  U  T  M  Q  R  X  O  S  <-- swap(a[0], a[0])   next pivot : a[0]
    #3:4            A  C  E  I  E  K  L  P  U  T  M  Q  R  X  O  S  <-- swap(a[3], a[4])   next pivot : a[4]
    #6:15           A  C  E  E  I  K  L  P  U  T  M  Q  R  X  O  S  <-- swap(a[6], a[6])   next pivot : a[6]
    #7:15           A  C  E  E  I  K  L  P  U  T  M  Q  R  X  O  S  <-- swap(a[8], a[14])
    #7:15           A  C  E  E  I  K  L  P  O  T  M  Q  R  X  U  S  <-- swap(a[9], a[10])
    #7:15           A  C  E  E  I  K  L  P  O  M  T  Q  R  X  U  S  <-- swap(a[7], a[9])   next pivot : a[9]
    #7:8            A  C  E  E  I  K  L  M  O  P  T  Q  R  X  U  S  <-- swap(a[7], a[7])   next pivot : a[7]
    #10:15          A  C  E  E  I  K  L  M  O  P  T  Q  R  X  U  S  <-- swap(a[13], a[15])
    #10:15          A  C  E  E  I  K  L  M  O  P  T  Q  R  S  U  X  <-- swap(a[10], a[13]) next pivot : a[13]
    #10:12          A  C  E  E  I  K  L  M  O  P  S  Q  R  T  U  X  <-- swap(a[10], a[12]) next pivot : a[12]
    #10:11          A  C  E  E  I  K  L  M  O  P  R  Q  S  T  U  X  <-- swap(a[10], a[11]) next pivot : a[11]
    #14:15          A  C  E  E  I  K  L  M  O  P  Q  R  S  T  U  X  <-- swap(a[14], a[14]) next pivot : a[14]
    After  sorting: A  C  E  E  I  K  L  M  O  P  Q  R  S  T  U  X  
    
    Total loop times : 69
    Total swap times : 17

    以上排序过程截图如下(截图来源: Algorithms Fourth Edition P289)

    小结: 快排序是一种不稳定的排序算法。其时间复杂度和空间复杂度是:

    Worst-case performance       O(N**2)
    Best-case  performance       O(N * logN)
    Average    performance       O(N * logN)
    Worst-case space complexity  O(N) auxiliary (naive)
                                 O(logN) auxiliary

    参考资料:

    1. https://interactivepython.org/courselib/static/pythonds/SortSearch/TheQuickSort.html
    2. http://www.code2learn.com/2013/02/quick-sort-algorithm-tutorial.html
    3. http://me.dt.in.th/page/Quicksort/

    改进的快排序

    在前面的讨论中,我们每次选取当前子表中的第一个对象作为基准对象(Pivot)。更好的选择是从第一个、中间一个与最后一个三者中选取关键字居中的对象作为基准。即pivot = median{Kl, K(l+r)/2, Kr}。 例如: median(10, 5, 7) = 7, median(10, 7, 7) = 7。

    到此为止,我们已经弄明白了快排序。下一节,将介绍直接插入排序的改进版--希尔排序。

  • 相关阅读:
    ural 1080 Map Coloring DFS染色
    hdu 4287 Intelligent IME
    hdu 4268 Alice and Bob 区域赛 1002 (STL、SBT实现)
    SBT专题训练
    hdu 4276 The Ghost Blows Light 区域网络赛 1010 树上背包+spfa
    hdu 4278 Faulty Odometer
    hdu 4279 Number
    VIM 插件(转)
    Linux环境变量的设置(转)
    福昕PDF阅读器 v3.3 破解
  • 原文地址:https://www.cnblogs.com/idorax/p/6545143.html
Copyright © 2020-2023  润新知