快速排序
属于原地排序,与自顶向下归并类似的是,也是先进行大数组的排序,然后递归到小数组,在进行排序时,先选定一个元素为切分元素,按大小将其他元素放到左右两侧即可
步骤:
- 确定切分元素,定义两个位于数组首尾的左右扫描指针
- 左指针遇到大于等于切分元素的元素时,停止移动;右指针遇到小于等于切分元素的元素时,停止移动;
- 当左右指针都停止时,交换左右指针所在元素
- 左右指针继续移动,交换,直到两者重合
- 重合后,重合位置元素与切分元素交换位置
- 再对切分元素左侧和右侧的元素分别进行快速排序,依次循环进行
动画演示: P为切分元素 L为左标记 R为有标记
package Sort;
import edu.princeton.cs.algs4.StdRandom;
import static Sort.SortExample.*;
import static edu.princeton.cs.algs4.StdRandom.uniform;
import static edu.princeton.cs.algs4.StdRandom.validateNotNull;
public class QuickSort {
// 主函数
public static void sort(Comparable[] a){
StdRandom.shuffle(a); // 调用了algs4.jar 中的源代码
sort(a, 0, a.length-1);
if(!isSorted(a))
System.out.println("快速排序失败");
}
// 为algs4.jar 中的源代码
// 将数组再次打乱 减少了对输入的依赖
public static void shuffle(Object[] a) {
validateNotNull(a); // 判断数组是否为空
int n = a.length;
for (int i = 0; i < n; i++) {
int r = i + uniform(n-i); // between i and n-1
// 交换位置操作
Object temp = a[i];
a[i] = a[r];
a[r] = temp;
}
}
// 为 algs4.jar 中的源代码
private static void validateNotNull(Object x) {
if (x == null) {
throw new IllegalArgumentException("argument is null");
}
}
private static void sort(Comparable[] a, int lo, int hi){
if(hi <= lo)
return;
int j = partition(a, lo, hi);
sort(a, lo, j-1);
sort(a, j+1, hi);
}
// 将数组分为 a[lo...,i-1] a[i] a[i+1..hi]
private static int partition(Comparable[] a, int lo, int hi){
int i = lo, j = hi+1; // 定义左右扫描指针
Comparable v = a[lo]; // 定义切分元素
// 扫描左右,检查扫描是否结束并交换元素
while (true){
while (less(a[++i], v)) // 左标记不断向右移动,直到有元素>=切分元素
if(i == hi) // 当左标记到达最右端,跳出循环
break;
while (less(v, a[--j])) // 右标记不断向左移动,直到有元素<=切分元素
if(j == lo) // 当右标记到达最左端,跳出循环
break;
if(i >= j) // 当左标记与右标记重合,跳出大循环
break;
exch(a, i, j); // 当左右标记满足条件停止移动时,交换标记所在的元素
}
exch(a, lo, j); // 将 v= a[j] 放入正确的位置
return j; // 返回切分元素索引,此时a[j] 左右两侧完成排序
}
}
特点:
- 快速排序如同名字一样,在大多数情况下,具有良好的性能优势
- 大多数编程语言的数组排序内置函数都采用快速排序实现
注意点:
- 当有元素值与切分元素重复时,需要防止循环意外中断
- 保持随机性,不依赖输入,尽量将切分元素也进行随机化
时间复杂度:
N
l
g
N
NlgN
NlgN
四种排序算法的比较
数据来源:algo4 官方提供的 algo4-data.zip 数据包
进行1k数据量的比较:
选择排序,1000个数据,执行时间为:0.0115291 s
插入排序,1000个数据,执行时间为:0.0105386 s
希尔排序,1000个数据,执行时间为:0.0016667 s
自顶向下归并排序,1000个数据,执行时间为:0.0017786 s
自底向上归并排序,1000个数据,执行时间为:6.685E-4 s
快速排序,1000个数据,执行时间为:0.0027377 s
进行32k数据量的比较:
选择排序,32000个数据,执行时间为:4.200542 s
插入排序,32000个数据,执行时间为:2.5466117 s
希尔排序,32000个数据,执行时间为:0.0212745 s
自顶向下归并排序,32000个数据,执行时间为:0.0246908 s
自底向上归并排序,32000个数据,执行时间为:0.0140204 s
快速排序,32000个数据,执行时间为:0.021905 s
其他有关排序算法的文章:
选择排序,插入排序,希尔排序的详解与比较