算法笔记－排序02:归并排序,快速排序

原地归并的抽象方法

``````public static void merge(Comparable[] a, int low, int mid, int high){
int i = low, j = mid+1;

for (int k = low; k <= high; k++)
indexA[k] = a[k];

for (int k = low; k <= high; k++){
if (i > mid) a[k] = indexA[j++];
else if (j > high) a[k] = indexA[i++];
else if (less(indexA[j], indexA[i])) a[k] = indexA[j++];
else a[k] = indexA[i++];
}
}
``````
自顶向下的归并排序

``````public class Merge {

private static Comparable[] indexA;

public static void merge(Comparable[] a, int low, int mid, int high){

int i = low, j = mid+1;

for (int k = low; k <= high; k++)
indexA[k] = a[k];

for (int k = low; k <= high; k++){
if (i > mid) a[k] = indexA[j++];
else if (j > high) a[k] = indexA[i++];
else if (less(indexA[j], indexA[i])) a[k] = indexA[j++];
else a[k] = indexA[i++];
}
}

public static void sort(Comparable[] a, int low, int high){
if (indexA==null) indexA = new Comparable[a.length];
if (high <= low) return;
int mid = low + (high-low)/2;
sort(a, low, mid);
sort(a, mid+1, high);
merge(a, low, mid, high);
}

private static boolean less(Comparable v, Comparable w){
return v.compareTo(w) < 0;
}

private static void exchange(Comparable[] a, int i, int j){
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(Comparable[] a){
for (int i = 0; i < a.length; i++)
System.out.print(a[i]);
System.out.println();
}

public static boolean isSorted(Comparable[] a){
for (int i = 1; i < a.length; i++){
if (less(a[i],a[i-1])) return false;
}
return true;
}
}
``````

自底向上的归并排序

``````public class MergeBU {
private static Comparable[] indexA;

public static void merge(Comparable[] a, int low, int mid, int high){

int i = low, j = mid+1;

for (int k = low; k <= high; k++)
indexA[k] = a[k];

for (int k = low; k <= high; k++){
if (i > mid) a[k] = indexA[j++];
else if (j > high) a[k] = indexA[i++];
else if (less(indexA[j], indexA[i])) a[k] = indexA[j++];
else a[k] = indexA[i++];
}
}

public static void sort(Comparable[] a){
if (indexA == null) indexA = new Comparable[a.length];
for (int sz = 1; sz<a.length; sz = sz+sz)
for (int low = 0; low < a.length-sz; low += sz+sz)
merge(a,low,low+sz-1,Math.min(low+sz+sz-1, a.length-1));
}

public static void main(String[] args){
Integer[] a = {9,8,7,6,5,4,3,2,1};
sort(a);
for (Integer i: a){
System.out.println(i);
}
}

private static boolean less(Comparable v, Comparable w){
return v.compareTo(w) < 0;
}

private static void exchange(Comparable[] a, int i, int j){
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(Comparable[] a){
for (int i = 0; i < a.length; i++)
System.out.print(a[i]);
System.out.println();
}

public static boolean isSorted(Comparable[] a){
for (int i = 1; i < a.length; i++){
if (less(a[i],a[i-1])) return false;
}
return true;
}
}
``````

快速排序

快速排序思路：

1.首先选择一个中间元素（一般选左端或者右端）。
2.分别获取除中间元素外的左右两端的索引。
3.由左右两端逐渐向中间迭代，每迭代一步比较一下索引中的元素和中间元素，当左边出现比中间元素大的元素的时候，暂停左边的迭代，当右边迭代出比中间元素小的元素的时候，右边迭代也暂停，交换左右两边的元素。
4.重复步骤3，直到左右两边的索引相遇，然后将中间元素移动到中间，这时中间元素左边的元素都比它小，右边的元素都比它大。
5.将上面的中间元素左右两边当成两个数组，分别进行上述过程。
6.重复以上步骤直到数组不可再分。

完整代码
``````public class Quick {

public static void sort(Comparable[] a){
sort(a,0,a.length-1);
}

private static void sort(Comparable[] a,int low, int high){
if (high <= low) return;
int j = partition(a,low,high);
sort(a,low,j-1);
sort(a,j+1,high);
}

private static int partition(Comparable[] a, int low, int high){
//将数组切分为a[lo..i-1], a[i], a[i+1..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[lo..j-1] <= a[j] <= a[j+1..hi]达成
}

public static void main(String[] args){
Integer[] a = {9,8,7,6,5,4,3,2,1};
sort(a, 0, a.length-1);
for (Integer i: a){
System.out.println(i);
}
}

private static boolean less(Comparable v, Comparable w){
return v.compareTo(w) < 0;
}

private static void exchange(Comparable[] a, int i, int j){
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(Comparable[] a){
for (int i = 0; i < a.length; i++)
System.out.print(a[i]);
System.out.println();
}

public static boolean isSorted(Comparable[] a){
for (int i = 1; i < a.length; i++){
if (less(a[i],a[i-1])) return false;
}
return true;
}
}
``````

``````%   java Main Merge Quick 10000 1000
``````

但是依然有人找到了一些有用的改进方式：

1.第一种改进方案是说由于插入排序在小数组的时候会比快速排序快，所以在分成小数组的时候使用插入排序，然而笔者在自己的电脑上测试的时候发现无论是大数组还是小数组，快速排序都比插入排序要快得多，按照这种方式修改的快速排序也变慢了，所以存疑。

2.实际应用中我们排序的数组常常含有大量的重复元素，例如将上千万人员的资料按照生日排序，那就必然会有大量的重复的数值(毕竟一百年里面也就四万多天，分配给上千万人作生日，自然有大量重复)，于是有人提出与其将数组二分，不如分成三部分，一部分小于中间值，一部分大于中间值，一部分等于中间值，此算法被称为三向切分的快速排序，以下是代码：

``````public class Quick3Way {

public static void sort(Comparable[] a){
sort(a,0,a.length-1);
}

private static void sort(Comparable[] a,int low, int high){
if (high <= low) return;
int lt = low, i = low+1,gt = high;
Comparable v = a[low];
while (i <= gt){
int cmp = a[i].compareTo(v);
if      (cmp < 0)exchange(a,lt++,i++);
else if (cmp > 0)exchange(a,i,gt--);
else    i++;
}
sort(a,low,lt-1);
if (gt<high) sort(a,gt+1,high);
}

public static void main(String[] args){
Integer[] a = {9,8,7,6,5,4,3,2,1};
sort(a, 0, a.length-1);
for (Integer i: a){
System.out.println(i);
}
}

private static boolean less(Comparable v, Comparable w){
return v.compareTo(w) < 0;
}

private static void exchange(Comparable[] a, int i, int j){
Comparable t = a[i];
a[i] = a[j];
a[j] = t;
}

private static void show(Comparable[] a){
for (int i = 0; i < a.length; i++)
System.out.print(a[i]);
System.out.println();
}

public static boolean isSorted(Comparable[] a){
for (int i = 1; i < a.length; i++){
if (less(a[i],a[i-1])) return false;
}
return true;
}
}
``````

总结一下当前学习过的排序算法的速度：

Quick > Merge > Shell > Insertion > Selection

Algorithm