快速排序学习笔记
2018-01-29 21:28
155 查看
最近在复习算法知识,到快速排序这里有一点新的想法,遂记录下来。
以上是最从最普通的快速排序思想,添加了一些小优化得到的,具体优化细节在注释中有体现,就不再赘述。
很明显,对快速排序理解的比较深刻的同学肯定能看出这样的快排在重复元素比较多的时候,复杂度O(logn)的快排会退化到复杂度为O(n²)的选择排序,这是因为每一个分治的时候,重复元素过多,导致分治的区域一边大一边小,非常不平衡,极端的情况就变成一边为0个元素,一边为所有元素,这种情况是非常不理想的。
所以,我们自然会想如何来处理重复元素,学过快速排序的同学肯定知道三路排序能解决这个问题,但是我只需要在这个算法中稍作优化,就能达到三路排序差不多的效果。
就是这样,当我们划左右区域的时候,将等于比较元素的值也一起放在左边,然后重新划分区域的时候先从左边区域最右边的元素开始比较,如果等于当前比较的元素,那么这个位置的元素被认为是有序的,不需要划分到递归排序的区域中的,这样就解决了重复元素的问题。
当然,这样的处理还是会比三路排序要慢,为什么呢,因为这种做法并不能保证所有的重复元素一定在一起,最极端的情况就是每一个与当前比较的元素相同的元素都是相隔一个的,这个时候虽然没有退化到O(n²),但是也没有O(logn)这么快了。而多路排序是可以通过一次扫描就将所有相同的元素一定放在一起的。
同样,可以增加一个指针,将相同的元素和小于的元素分隔开来,这样也能达到三路排序的效果,但是这样做的话本质上就和三路排序没有区别了。
https://github.com/cechaodeng/dataStruct/tree/master/src
/** * 将[left,right]区间快速排序 * 基准值是固定的,这种情况还有优化空间 * @param arr * @param left * @param right */ private static void partition(Comparable[] arr, int left, int right) { /* 优化一:当元素个数非常小的时候,最有可能接近有序,这时候用插入排序最快 */ /*if (left >= right) { return; }*/ if (right - left < 16) { InsertionSort.sort(arr, left, right); return; } /** * 优化二: * 基本值的位置随机,然后将该随机位置的值与l位置交换一下就可以无缝连接 */ int baseIndex = (int) (Math.random() * (right - left + 1)) + left; SortTestHelper.swap(arr, baseIndex, left); int l = left;//用于比较的基本值的位置 int j = left;//小于基本值的最右边元素的位置,初始状态应该指向left-1,表示没有 for (int i = l + 1; i <= right; i++) {//当前遍历到的位置 if (arr[i].compareTo(arr[l]) < 0) { //小于 //swap SortTestHelper.swap(arr, j + 1, i); j++; } } //走完一遍,将用于比较的基本值与j位置交换 SortTestHelper.swap(arr, l, j);//交换后,j就是基本元素的位置,小于基本元素的最右边的位置变成了j-1 partition(arr, left, j - 1); partition(arr, j + 1, right); }
以上是最从最普通的快速排序思想,添加了一些小优化得到的,具体优化细节在注释中有体现,就不再赘述。
很明显,对快速排序理解的比较深刻的同学肯定能看出这样的快排在重复元素比较多的时候,复杂度O(logn)的快排会退化到复杂度为O(n²)的选择排序,这是因为每一个分治的时候,重复元素过多,导致分治的区域一边大一边小,非常不平衡,极端的情况就变成一边为0个元素,一边为所有元素,这种情况是非常不理想的。
所以,我们自然会想如何来处理重复元素,学过快速排序的同学肯定知道三路排序能解决这个问题,但是我只需要在这个算法中稍作优化,就能达到三路排序差不多的效果。
for (int i = l + 1; i <= right; i++) {//当前遍历到的位置 if (arr[i].compareTo(arr[l]) <= 0) {//加上等于,把等于该数的都放在一起来 //小于 //swap SortTestHelper.swap(arr, j + 1, i); j++; } } //走完一遍,将用于比较的基本值与j位置交换 SortTestHelper.swap(arr, l, j);//交换后,j就是基本元素的位置,小于基本元素的最右边的位置变成了j-1 //等于基本值的已经不需要处理了 for (int k = j - 1; k >= left; k--) { if (arr[k].compareTo(arr[j]) != 0) { //不等于的时候,这里需要分治了 partition2(arr, left, k); break; } } //partition2(arr, left, j - 1); partition2(arr, j + 1, right);
就是这样,当我们划左右区域的时候,将等于比较元素的值也一起放在左边,然后重新划分区域的时候先从左边区域最右边的元素开始比较,如果等于当前比较的元素,那么这个位置的元素被认为是有序的,不需要划分到递归排序的区域中的,这样就解决了重复元素的问题。
当然,这样的处理还是会比三路排序要慢,为什么呢,因为这种做法并不能保证所有的重复元素一定在一起,最极端的情况就是每一个与当前比较的元素相同的元素都是相隔一个的,这个时候虽然没有退化到O(n²),但是也没有O(logn)这么快了。而多路排序是可以通过一次扫描就将所有相同的元素一定放在一起的。
同样,可以增加一个指针,将相同的元素和小于的元素分隔开来,这样也能达到三路排序的效果,但是这样做的话本质上就和三路排序没有区别了。
https://github.com/cechaodeng/dataStruct/tree/master/src
相关文章推荐
- python数据结构学习笔记-2016-11-24-01-快速排序
- 学习笔记----快速排序的java实现及其改良
- 快速排序--学习笔记
- 快速排序学习笔记
- 基础算法学习笔记—快速排序
- 快速排序(Quicksort)学习笔记
- 数据结构 学习笔记(十一):排序(下):快速 / 表 / 桶 / 基数 排序,排序算法的比较
- C++学习笔记(二)——快速排序的库函数实现
- 学习笔记之快速排序——quicklySort——基础算法——java
- 算法导论学习笔记(一)快速排序及优化
- 算法导论学习笔记(五):快速排序
- 快速排序学习笔记
- 学习笔记:快速排序的C++、JavaScript(2种方法)、Java实现
- 黑马程序员之数据结构学习笔记:快速排序
- 算法学习笔记之快速排序
- 算法学习笔记--排序之快速排序
- 算法导论学习笔记 第7章 快速排序
- 算法导论学习笔记-第7章 快速排序
- 8大内部排序算法学习笔记--(2)快速排序 Java实现
- 算法第四版学习笔记之快速排序 QuickSort