• 三路快速排序算法


    1、三路快速排序算法的基本思想

    之前的快速排序算法都是将序列分成<=v和>v或者是<v和>=v的两个部分,而三路快速排序是

    将序列分成三个部分:<v、=v、>v,如下图所示:

    首先v元素还是作为"基准"元素,e表示当前遍历索引值指向的元素,也就是待考虑的元素,从图中可以

    看出来,整个序列被分成3个部分,也就是说当我们遍历完成之后整个序列就已经被分成了<v、=v、>v

    三个部分了,而我们只需要对<v和>v的两个部分再次递归调用三路排序函数进行排序即可,来看看具体

    的过程:

    首先来看看整个序列的布局,这里我们使用了3个索引值来表示3个不同的位置,使用lt索引来表示

    <v部分和=v部分的分界处(这里选的是<v部分的最后一个元素),使用i索引表示遍历的位置,

    使用gt索引来表示=v部分和>v部分的分界处(这里选的是>v部分的第一个元素)。

     如果当前i指向的元素=v,那直接就将此元素归为=v部分,i++即可

     如果当前i指向的元素<v,将此元素与=v部分的第一个元素

                                                                   交换位置,也就是swap(arr[i], arr[lt+1]),之后将lt++,

                                                                   i++即可,此时<v部分就多了一个元素

     如果当前i指向的元素>v,则将此元素与>v部分的第一个元

                                                                  素的前一个元素交换位置,也就是swap(arr[i], arr[gt-1]),

                                                                  然后gt--,表示>v部分多了一个元素此时i不用动,因为他指

                                                                  向的元素是交换过来的,这个元素还没有遍历到。

     当gt与i重合的时候就表示便利完毕了,那么此时我们只需要

                                                                   将l指向的元素v与lt交换位置即可,之后就如下所示了

     

    之后我们只需要对<v和>v这两部分再次递归进行三路排序,而对于=v的部分就不用在考虑了,因为他们

    已经放在了合适的位置了,所以从这里可以看出来如果=v部分元素非常多,那么我们的三路快速排序算法

    效果就会越明显,这也正是他的优点所在。

    2、三路快速排序算法的实现(基于C++)

    首先说明,代码中使用到的索引值变量都是取之于上面的图示中,这样便于描述问题

     1 /**************************************三路快速排序算法实现***********************************/
     2 template<typename T>
     3 void __quickSort3Ways (T arr[], int left, int right)
     4 {
     5     if (right - left <= 40) {                  /* 对递归到数据量较小时使用插入排序 */
     6         __insertSortMG<T>(arr, left, right);
     7         return;
     8     }
     9 
    10     std::swap(arr[left], arr[std::rand() % (right - left + 1) + left]);  // 随机化找到一个元素作为"基准"元素 
    11     T v = arr[left];
    12 
    13     int lt = left;       // 将<v的分界线的索引值lt初始化为第一个元素的位置(也就是<v部分的最后一个元素所在位置)
    14     int gt = right + 1;  // 将>v的分界线的索引值gt初始化为最后一个元素right的后一个元素所在位置(也就是>v部分的第一个元素所在位置)     
    15     int i = left + 1;    // 将遍历序列的索引值i初始化为 left+1
    16     
    17     while (i < gt) {     // 循环继续的条件
    18         if (arr[i] < v) {
    19             std::swap(arr[i], arr[lt + 1]);  // 如果当前位置元素<v,则将当前位置元素与=v部分的第一个元素交换位置 
    20             i++;                             // i++  考虑下一个元素
    21             lt++;                            // lt++  表示<v部分多了一个元素
    22         }
    23         else if (arr[i] > v) {               // 如果当前位置元素>v,则将当前位置元素与>v部分的第一个元素的前一个元素交换位置
    24             std::swap(arr[i], arr[gt - 1]);  // 此时i不用动,因为交换过来的元素还没有考虑他的大小
    25             gt--;                            // gt--  表示>v部分多了一个元素
    26         }
    27         else {              //  如果当前位置元素=v   则只需要将i++即可,表示=v部分多了一个元素
    28             i++;
    29         }
    30     }
    31 
    32     std::swap(arr[left], arr[lt]);   // 上面的遍历完成之后,将整个序列的第一个元素(也就是"基准"元素)放置到合适的位置 
    33                                      // 也就是将它放置在=v部分即可
    34     __quickSort3Ways<T>(arr, left, lt - 1); // 对<v部分递归调用__quickSort3Ways函数进行三路排序
    35     __quickSort3Ways<T>(arr, gt, right);    // 对>v部分递归调用__quickSort3Ways函数进行三路排序
    36 }
    37 
    38 template<typename T>
    39 void quickSort3Ways (T arr[], int count)
    40 {
    41     std::srand(std::time(NULL));               /* 种下随机种子 */
    42     __quickSort3Ways<T>(arr, 0, count - 1);    /* 调用__quickSort3Ways函数进行三路快速排序 */
    43 }
    44 /***********************************************************************************************/

    3、性能测试

    一般排序序列:

    大量重复元素序列:

    近乎有序状态的序列:

  • 相关阅读:
    System.out.println与System.err.println的区别
    数组及引用类型内存分配
    数组及引用类型内存分配
    JAVA内存分配-通俗讲解
    JAVA内存分配-通俗讲解
    java中abstract怎么使用
    MyEclipse 快捷键
    SQLite -创建表
    配置Hexo
    Dykin's blog
  • 原文地址:https://www.cnblogs.com/deng-tao/p/6536302.html
Copyright © 2020-2023  润新知