[算法简结]递归分治(三):快速排序

  今天我们讲讲和合并排序一样也是运用分治的排序算法:快速排序。而且它们的时间复杂度都能到达一个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、进一步优化

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

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

 

你可能感兴趣的:(快速排序)