快速排序(Quick Sort)介绍
2016-05-04 14:47
423 查看
原理介绍
快速排序是分治法(Divide and Conquer)的典型应用,基本思想是在数组中适当的轴心(partition),然后将数组一分为二,再分别对左边和右边数组进行排序,而影响快速排序法效率的正是轴心的选择。快速排序根据分割点的不同,有三种版本:
选择最左端的数为比较对象,低指针从左边开始向右移动,高指针从右边开始向左移动;
选择中间的数为比较对象,低指针从左边开始向右移动,高指针从右边开始向左移动;
选择最右端的数为比较对象,低指针和高指针都从左端开始移动。
快排的三种版本
下面分别介绍三种快速排序的版本。第一种版本:
(1)将最左边的数设定为轴,其值为val
循环:
(2)令索引i 从数组左方往右方找,直到找到大于 val 的数
(3)令索引 j 从数组右方往左方找,直到找到小于 val 的数
(4)如果 i >= j,则离开循环,否则,交换索引 i 与 j 两处的值
(5)将左侧的轴与 j 进行交换
(6)对轴左边(j-1)的数组进行递归处理
(7)对轴右边(j+1)的数组进行递归处理
public static void QuickSort(int[] a, int lo, int hi) { if (lo >= hi) return; int i = lo; int j = hi + 1; while (true) { while ((a[++i] < a[lo])) if (i == hi) break; while ((a[--j] > a[lo])) if (j == lo) break; if (i >= j) break; exch(a, i, j); } exch(a, lo, j); QuickSort(a, lo, j-1); QuickSort(a, j+1, hi); }
第二种版本:
(1)将中间的数val设为轴
循环:
(2)令索引i 从数组左方往右方找,直到找到大于 val 的数
(3)令索引 j 从数组右方往左方找,直到找到小于 val 的数
(4)如果 i >= j ,则离开循环,否则,交换索引 i 与 j 两处的值
(5)对轴左边(i-1)的数组进行递归处理
(6)对轴右边(j+1)的数组进行递归处理
边界正确性证明:
(1)若 i == j ,则 i-1 和 j+1 正好是某个数两边的界限
(2)若 i > j ,则不难知道 i-1 == j (即 i == j+1),则 i-1 与 j+1 正好是两个子数组的边界
public static void QuickSort (int[] a, int lo, int hi) { if (lo >= hi) return; int i = lo-1, j = hi+1; int mid = (lo + hi) / 2; int val = a[mid]; while (true) { while (a[++i] < val) if (i == hi) break; while (a[--j] > val) if (j == lo) break; if (i >= j) break; exch(a, i, j); } QuickSort(a, lo, j); QuickSort(a, j+1, hi); }
第三种版本:
(1)将最右边的数 val 设为轴
循环:
(2)令索引 j 从数组左方开始往右方找,直到找到小于 val的数
(3)若 j 小于高位hi ,则交换 i 和 j 位置的元素,然后 i++
(4)若j
大于或等于高位hi ,则退出循环
(5)交换 i位置的数和最右边的数
(6)对 i-1 左边的数组进行递归排序
(7)对i+1 右边的数组进行递归排序
public static void QuickSort (int[] a, int lo, int hi) { if (lo >= hi) return; int i = lo, j = lo-1; int val = a[hi]; while (true) { while (a[++j] >= val) if (j == hi) break; if (j >= hi) break; exch(a, i++, j); } exch(a, i, hi); QuickSort(a, lo, i-1); QuickSort(a, i+1, hi); }
含有重复键(duplicate key)的快排程序
若序列中含有大量重复键,若使用前面三种方法进行排序,会使得重复键参与到下一次排序中,极大地降低了排序算法的效率。Robert Sedgewick 提出了一种方法,使得重复键不参与下一次的排序中,提升了算法的效率。不过键交换的次数会增多。算法的步骤如下:
(1)设定三个指针分别为lt,i,gt。其中,在指针lt左边的数组值小于val,在lt到i之间的数组值等于val,在gt右边的数组值大于val,在i到gt之间的值是未处理的。
循环(i <= gt时):
(2)当索引i处的值小于val时,交换i 和lt的值
(3)当索引i处的值大于val时,交换i 和gt的值
(4)否则(当索引i处的值等于val时),i自增
(5)对lt左边的数组进行排序
(6)对gt右边的数组进行排序
public static void QuickSort_DuplicateKeys(int[] a, int lo, int hi) { if (lo >= hi) return; int lt = lo, gt = hi; int i = lt; int val = a[lo]; while (i <= gt) { if (a[i] < val) exch(a, i++, lt++); else if (a[i] > val) exch(a, i, gt--); else i++; } QuickSort_DuplicateKeys(a, lo, lt-1); QuickSort_DuplicateKeys(a, gt+1, hi); }
快排的特性
1.快排是一种内排序算法2.快排时间复杂度的最好情况是~NlgN,最坏情况是~(1/2)*N^2,平均情况是~1.39NlgN
3.快排的空间复杂度:对于Partitioning:需要常数级的额外空间;对于递归的深度:需要对数级的额外空间
快排的优化
1.在JDK API的数组排序算法中,会先对数组的长度进行判断。若是长度较短的数组,则直接使用插入排序。对于较长的数组才使用快速排序。2.Robert Sedgewick认为在快速排序之前,应先对数组进行Shuffle操作,以提高快速排序的性能。因为在最坏的情况下,快排的时间复杂度是~(1/2)*N^2。而平均情况下,快排的时间复杂度是~1.39NlgN。
相关文章推荐
- java循环结构(以及break,continue的使用)
- Call requires API level 3 (current min is 1)
- UITableView的优化技巧
- iOS中UISearchBar(搜索框)使用总结
- AbstractSequentialList源码分析
- StringBuilder、StringBuffer和String的关系与区别
- XMG 重写- (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event方法
- UIWindow & UIWindowLevel笔记
- 2016.05.04,英语,《Vocabulary Builder》Unit 22
- UIImage的两种初始化方法的区别
- UiAutomator Android 的自动测试框架(基础)
- Gradle version 2.2 is required. Current version is 2.10
- dispatch queue GCD
- UIAlertAction添加输入框
- UIPickerView 选择控制器----ios控件 UIPickerView应用 制作简易省、市、地区选择器
- 53. Maximum Subarray My Submissions QuestionEditorial Solution
- XMG 调用UIWindow hitTest方法
- 【Arduino官方教程第一辑】示例程序 2-9 制作你的特雷门琴
- 学习Handler、MessageQueue、Looper及其之间的关系
- java String、StringBuffer、StringBuilder