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

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

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

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

原文地址:https://www.cnblogs.com/edwardstudy/p/2869612.html