快速排序
实现原理:递归的将切分元素的左边和右边排序,优点是原地排序,而且速度快,时间复杂度是线性对数的
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
public class Quick {
public static void sort(Comparable[] a){
StdRandom.shuffle(a); //消除对输入的依赖,保持随机性的另一种方法是在partition中随机选取一个切分元素
sort(a, 0, a.length - 1);
}
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); //将有半部分排序
}
private static int partition(Comparable[] a, int lo, int hi) {
// TODO Auto-generated method stub
int i = lo, j = hi + 1;
Comparable v = a[lo];
while(true){
while(less(a[++i],v)) //获得比v大的数
if(i == hi)
break;
while(less(v, a[--j])) //获得比v小的数
if(j == lo)
break;
if(i >= j) //当i和j相遇时退出循环
break;
exch(a, i, j);
}
exch(a, lo, j);
return j;
}
private static void exch(Comparable[] a, int i, int j) {
// TODO Auto-generated method stub
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static boolean less(Comparable v, Comparable w) {
// TODO Auto-generated method stub
return v.compareTo(w) < 0;
}
public static void show(Comparable[] a) {
// TODO Auto-generated method stub
for(int i = 0; i <a.length;i++ )
StdOut.print(a[i] + " ");
StdOut.println();
}
}
但是当数组过小时,快速排序的性能并不见得回比插入排序好,所以在递归时,判断数组大小是否小于某一个合适的常数,当小于这个常数时使用插入排序。将私有方法sort()进行改进。其他部分保持不变。
改进代码如下:
private static void sort(Comparable[] a, int lo, int hi){
if(hi <= lo + 10){ //对于小数组使用插入排序性能比快速排序更好
Insertion.sort(a, lo, hi);
return;
}
int j = partition(a, lo, hi);
sort(a, lo, j - 1); //将左半部分排序
sort(a, j + 1, hi); //将有半部分排序
}
上面的优化解决了当数组过小时,性能较差的问题,但是当数组中重复元素很多时,快速排序任然会把大量时间话在对相等元素进行排序,下面这段代码是想了三向切分快速排序。它将数组分为小于切分元素,等于切分元素,大于切分元素三个部分,然后对小于和大于的部分进行排序,而等于的部分不动。
import edu.princeton.cs.algs4.StdOut;
import edu.princeton.cs.algs4.StdRandom;
public class Quick3way {
public static void sort(Comparable[] a){
StdRandom.shuffle(a); //消除对输入的依赖,保持随机性的另一种方法是在partition中随机选取一个切分元素
sort(a, 0, a.length - 1);
}
private static void sort(Comparable[] a, int lo, int hi) {
// TODO Auto-generated method stub
if(hi <= lo)
return;
int lt = lo,i = lo + 1, gt = hi; //a[lo...lt-1]小于v,a[lt...i-1]等于1,a[i...gt]不确定,a[gt+1...hi]大于1
Comparable v = a[lo];
while(i <= gt){
int cmp = a[i].compareTo(v);
if(cmp < 0)
exch(a,lt++, i++); //将a[lt]和a[i]交换
else if(cmp > 0)
exch(a, i, gt--); //i不需要在进行自增操作,因为a[gt]这个元素还没有和v比较
else i++;
}
sort(a, lo, lt - 1); //对小于v的元素进行排序
sort(a, gt + 1, hi); //对大于v的元素进行排序
}
private static void exch(Comparable[] a, int i, int j) {
// TODO Auto-generated method stub
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}
private static boolean less(Comparable v, Comparable w) {
// TODO Auto-generated method stub
return v.compareTo(w) < 0;
}
public static void show(Comparable[] a) {
// TODO Auto-generated method stub
for(int i = 0; i <a.length;i++ )
StdOut.print(a[i] + " ");
StdOut.println();
}
}