zoukankan      html  css  js  c++  java
  • [算法简结]递归分治(三):快速排序

      今天我们讲讲和合并排序一样也是运用分治的排序算法:快速排序。而且它们的时间复杂度都能到达一个O(nlgn)的上界。所以我们都能在n个元素的情况下,是算法以Ω(nlgn)时间运行。

    1、基本思想

      合并排序和快速排序的不同就在于递归调用发生的地方。合并排序先将集合分成两个子集在把子集分成更小的子集,所以递归调用发生在处理整个集合之前,而快速排序则是先将两个子集独立地排序,所以递归调用方发生在处理整个集合之后。而怎么划分两个子集则是快速排序的关

    以最后一个元素划分

    快速排序的API

    void quickSort(E[] list) 算法的入口
    quickSort(E[] list, int first, int last)           排序从low到high的元素
    partition(E[] list, int first, int last) 在first与last之间以某个元素划分               

     

    2、Normal难度下的快速排序

      这里的划分只是普普通通地要求当前集合的第一个元素罢了,所以实现起来也普普通通。

    partition
     1     private static <E extends Comparable> int partition(E[] list, int first, int last)
     2     {
     3         E pivot = list[first];
     4         int low = first;
     5         int high = last + 1;
     6         while(true)
     7         {
     8             while(list[++low].compareTo(pivot) == -1 && low < last);
     9             while(list[--high].compareTo(pivot) == 1);
    10             if(low >= high)
    11                 break;
    12             Swap(list, low, high);                
    13         }
    14         
    15         list[first] = list[high];
    16         list[high] = pivot;
    17         
    18         return high;
    19     }

    以最后一个元素划分与此类似,只不过要注意限界条件。

      quickSort就是简简单单的划分再递归,划分再递归...

    quickSort
    1     private static <E extends Comparable> void quickSort(E[] list, int first, int last)
    2     {
    3         if(first < last)
    4         {
    5             int pivotIndex = partition(list, first, list.length - 1);
    6             quickSort(list, first, pivotIndex  - 1);
    7             quickSort(list, pivotIndex + 1, last);
    8         }
    9     }

    3、随机取划分元素

      这种愣头地以第一个元素划分的方法效率高吗?假设一个已经有序的集合用第一个元素划分的情况,划分会形成两个极不对称子集合。并且每一步划分都会产生这样的结果。这样当然很麻烦,因为每次只是纯下标循环而没有元素交换。然而使用随机划分,这样左右子集合的比例就可以像Luffy的左右手比例一样变化。

    在调用partition前,先中随机取得一个元素与第一个元素交换即可

    randomizedPartition
    1     private static <E extends Comparable> int randomizedPartition(E[] list, int first, int last)
    2     {
    3         int i = (int)Math.random() * (first - last + 1) + first;
    4         Swap(list, i, first);
    5         
    6         return partition(list, first, last);
    7     }

    quickSort方法中做相应的修改

     int pivotIndex = randomizedPartition(list, first, last);

    3、三取样划分

      如果觉得随机化不是避免算法的最坏情况行为,而是设法消除这种最坏情况的行为与特定实例之间的关联性,仅仅只是是时间复杂度得到相对均匀。那么我们就实打实地利用计算中位数来优化快速排序。前人大量的实验数据发现将取样大小设为3并用大小相对居中的元素划分效果最好。

      我就选在第一个元素、中间的元素和最后一个元素中找中位数为划分元素。怎么找中位数不在本文叙述范围内

    median3

    简而言之,就是通过比较一级一级地找到三个数中间那个元素即可。

    所以三取样划分如下

    median3Partition

    4、进一步优化

      在实际运用当中经常会出现含有大量重复元素的数组,可以运用基数排序的原理大大减少比较的次数

      并且对于小数组,快速排序比插入排序盘,所以可以再数组范围比较小的时候进行插入排序(什么是插入排序?大家想想打扑克时是怎么抓牌的)。

  • 相关阅读:
    最长公共子序列
    最长重复子串—后缀数组
    最长递增子序列
    最长重复子串(转)
    最长递增子序列(转)
    最长不重复子串(转)
    连续子数组最大和(转)
    alert 与 console.log
    一个null,让浏览器SB
    javascript实现简单的动画功能
  • 原文地址:https://www.cnblogs.com/edwardstudy/p/2869612.html
Copyright © 2011-2022 走看看