• 快速排序算法


    1、什么是快速排序算法?

    快速排序是由东尼·霍尔所发展的一种排序算法,犹如他的名字一样,速度快,效率高,也是实际

    中最常用的一种算法,被称为20实际对世界影响最大的算法之一。

    基本思想:

    1): 从序列中挑出一个元素作为"基准"元素,一般是该序列的第一个元素或者是最后一个元素。

    2): 把序列分成2个部分,其数值大于"基准"元素的元素放在"基准"元素的左边,否在放在"基准"元

    素的右边,此时"基准"元素所在的位置就是正确的排序位置,这个过程被称为 partition(分区)。

    3): 递归将"基准"元素左边的序列和"基准"元素右边的序列进行partition操作。

    2、算法的演示

     这个就是待排序的数组序列,第一个元素作为"基准"元素

     给"基准"元素找到合适的位置,将比"基准"元素小的元素放在其左边,否则放在其右边

      至此这个序列就成了这样了,这个过程成为partition

    下面来看看partition的具体实现过程:

     将"基准"元素用v表示,使用i作为遍历序列的索引值,j的位置

                                                            表示>v部分和<v部分的分界位置(也就是最后一个小于v的元

                                                            素所在位置)。

         

     如果此时i指向的元素大于v,这个好处理,直接将i++即可,

                                                            也就表示大于v的元素多了一个

     如果此时i指向的元素小于v,那么需要将i指向的元素与大于

                                                             v序列的第一个元素交换位置,即swap(arr[i], arr[j+1]),

                                                             然后再将i++,再将j++即可,表示小于v的元素多了一个。

                                                             如下图所示

     进行swap(arr[i], arr[j+1])

      j++

     i++

     由此可知,当遍历完成之后,就会出现这样的效果,然后我

                                                              们只需将元素v与j指向的元素交换位置即可

     此时就出现了小于"基准"元素的元素在其左边,大于"基准"

                                                            元素的元素在其右边的分布情况

    3、算法实现的动画效果

    4、算法的实现(基于C++)

     1 /*********************************** 随机化快速排序算法实现 ****************************************/
     2 // 对arr[left....right]进行partition操作
     3 // 返回p,使得arr[left, p-1] < arr[p],arr[p+1, right] > arr[p]
     4 template<typename T>
     5 int __partition (T arr[], int left, int right)
     6 {
     7     T v = arr[left];          // 将第一个元素作为"基准"元素
     8     int j = left;             // 将<v部分的分界点位置j初始化为本序列中的第一个元素位置(也就是<v部分的最后一个元素位置)
     9 
    10     // 将遍历序列的索引值i初始为第二个元素位置
    11     for (int i = left + 1; i <= right; i++) {
    12         if (arr[i] < v) {     // 如果i指向的元素<v,那么将此元素与>v部分的第一个元素交换位置,然后j++,表示<v的元素又多了一个
    13             j++;              
    14             std::swap(arr[j], arr[i]);  // 这里采用了另一种那写法,因为j++之后指向的就是>v部分的第一个元素,交换位置之后其实类似于将>v的部分整体往右边移动了一个位置
    15         }
    16     }
    17 
    18     std::swap(arr[left], arr[j]);   // 遍历完成之后只需要将"基准"元素(也就是第一个元素)与当前j指向的位置交换位置即可
    19     return j;                       // 因为"基准"元素并不属于<v的部分,所以交换之后此时j指向的元素就是"基准"元素
    20 }
    21 
    22 // 对arr[left...right]范围内数组序列进行快速排序操作
    23 template<typename T>
    24 void __quickSort (T arr[], int left, int right)
    25 {
    26     int p = __partition<T>(arr, left, right);  // 对arr[left...right]区间元素进行partition操作,找到"基准"元素
    27     __quickSort<T>(arr, left, p - 1);          // 对基准元素之前的序列递归调用__quickSort函数
    28     __quickSort<T>(arr, p + 1, right);         // 对基准元素之后的序列递归调用__quickSort函数
    29 }
    30 
    31 template<typename T>
    32 void quickSort (T arr[], int count)
    33 {
    34     __quickSort<T>(arr, 0, count - 1);   // 调用__quickSort函数进行快速排序
    35 }
    36 /********************************************************************************************/ 

    5、算法性能测试

    与前面的2种归并排序算法相比较

    测试数据量100000:

    测试数据量1000000:

    从上面可以知道,很明显快速排序要比归并排序快,这还是在快速排序没有优化的情况下。不同的

    电脑由于配置不一样,可能得到的测试结果是不同的。

    6、快速排序算法的优化

    (1)第一种优化

    在前面的学习过程中已经说到了,那就是对于几乎所有的高级算法都可以使用的一种优化方法,

    当递归到元素个数很小时可以使用直接插入排序。

    (2)第二种优化(随机化快速排序算法)

    我们先来看一个例子,当待排序的数组序列接近为一个有序序列的时候,归并排序和快速排序的性能测试

    测试数据量500000:

    通过上面可以知道,当序列接近为有序状态的时候,快速排序慢得要死,这是为什么呢?请看下面的分析:

    对于快速排序,因为我们是将第一个元素作为"基准"元素,但由于序列基本接近为有序,从而导致每一个

    "基准"元素的左边没有一个元素,而全部

    在他的右边,这样就会导致快速排序算法的高度为n,将退化为O(n^2)级别。

    那么这种情况的解决办法就是: 尽可能的别让第一个元素成为"基准"元素,而最好使用中间位置的元素成为

    "基准"元素,那如何做到这点呢?

    解决办法就是"基准"元素随机产生,而不指定。请看下面的代码:

     1 /*********************************** 随机化快速排序算法实现 ****************************************/
     2 // 对arr[left....right]进行partition操作
     3 // 返回p,使得arr[left, p-1] < arr[p],arr[p+1, right] > arr[p]
     4 template<typename T>
     5 int __partition (T arr[], int left, int right)
     6 {
     7     std::swap(arr[left], arr[std::rand()%(right-left+1)+left]);  // 随机产生"基准"元素所在位置,并与第一个元素交换位置
     8 
     9     T v = arr[left];          // 将第一个元素作为"基准"元素
    10     int j = left;             // 将<v部分的分界点位置j初始化为本序列中的第一个元素位置(也就是<v部分的最后一个元素位置)
    11 
    12     // 将遍历序列的索引值i初始为第二个元素位置
    13     for (int i = left + 1; i <= right; i++) {
    14         if (arr[i] < v) {     // 如果i指向的元素<v,那么将此元素与>v部分的第一个元素交换位置,然后j++,表示<v的元素又多了一个
    15             j++;              
    16             std::swap(arr[j], arr[i]);  // 这里采用了另一种那写法,因为j++之后指向的就是>v部分的第一个元素,交换位置之后其实类似于将>v的部分整体往右边移动了一个位置
    17         }
    18     }
    19 
    20     std::swap(arr[left], arr[j]);   // 遍历完成之后只需要将"基准"元素(也就是第一个元素)与当前j指向的位置交换位置即可
    21     return j;                       // 因为"基准"元素并不属于<v的部分,所以交换之后此时j指向的元素就是"基准"元素
    22 }
    23 
    24 // 对arr[left...right]范围内数组序列进行快速排序操作
    25 template<typename T>
    26 void __quickSort (T arr[], int left, int right)
    27 {
    28     if (right - left <= 40) {      // 对归并到序列中元素个数较小时采用插入排序算法
    29         __insertSortMG<T>(arr, left, right);
    30         return;
    31     }
    32 
    33     int p = __partition<T>(arr, left, right);  // 对arr[left...right]区间元素进行partition操作,找到"基准"元素
    34     __quickSort<T>(arr, left, p - 1);          // 对基准元素之前的序列递归调用__quickSort函数
    35     __quickSort<T>(arr, p + 1, right);         // 对基准元素之后的序列递归调用__quickSort函数
    36 }
    37 
    38 template<typename T>
    39 void quickSort (T arr[], int count)
    40 {
    41     std::srand(std::time(NULL));         // 种下随机种子
    42     __quickSort<T>(arr, 0, count - 1);   // 调用__quickSort函数进行快速排序
    43 }
    44 /********************************************************************************************/

    这次的改变在于: 在quickSort函数中种下了随机种子,然后在__partition函数中使用rand函数来产生随机的位置,

    将此位置的元素作为"基准"元素,从而可以避免使用第一个元素作为"基准"。使用了随机化快速排序之后,虽然在排

    序一般的序列时会比之前的快速排序算法要慢,但是之前的快速排序算法对于一个近乎有序的序列时就不行了,而随

    机化快速排序就能够很好的解决这样的问题,所以随机化快速排序就能够兼顾这样两种不同的情况,而且还能够快速

    的对序列进行排序。来看看与归并排序算法之间的性能比较:

    一般序列数据量1000000:

    近乎有序的序列数据量1000000:

    从上面的测试得到的结果可以看出来,对于一般的序列使用随机化快速排序要比归并排序快,而对于近乎有序的序

    列明显归并排序要快,这是归并排序的一个优势,之前说过;但是实际中出现近乎有序序列的概率是很低很低的,

    所以完全可以认为随机化快速排序在总体上比归并排序快。

  • 相关阅读:
    堆和栈 的区别
    equals == 区别
    【知识点】Filter、Servlet、Listener区别与联系
    白盒测试相关的一些知识
    紧急情况下压缩了测试周期应该怎么办?
    软件性能测试与可靠性测试
    软件测试概念
    web测试方法总结
    结对测试探讨
    八种状态增加测试用例状态的精确度
  • 原文地址:https://www.cnblogs.com/deng-tao/p/6506006.html
Copyright © 2020-2023  润新知