1. Mergesort
步骤:
・将数组分为两半
・递归地给每一半进行排序
・融合两半的结果
1.1 Abstract in-place merge:Java实现
给两个排序完成的子数组,a[lo]到a[mid]以及a[mid+1]到a[hi],
用排序好的子数组a[lo]到a[hi]来替代。
使用一个辅助数组(auxilliary array)将原来的数组lo到hi的元素复制过来。然后创建三个pointer k,i,j分别指向原数组初始位置,辅助数组左半部分初始位置(lo)和右半部分初始位置(mid+1),比较i和j大小,把比较小的那个存到k的位置,然后把对应的pointer增加1指向下一位置,k也要增1指向下一位置。此外如果i或j中任何一个pointer首先到达了自己那半个数组的末端(mid或hi),则直接将另半个数组复制进元素组剩下位置即可。
private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi)
{
assert isSorted(a, lo, mid); // precondition: a[lo..mid] sorted
assert isSorted(a, mid+1, hi); // precondition: a[mid+1..hi] sorted
for (int k = lo; k <= hi; k++)
aux[k] = a[k];
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++)
{
if (i > mid) a[k] = aux[j++];
else if (j > hi) a[k] = aux[i++];
else if (less(aux[j], aux[i])) a[k] = aux[j++];
else a[k] = aux[i++];
}
assert isSorted(a, lo, hi); // postcondition: a[lo..hi] sorted
}
1.2 Merge sort:Java实现
public class Merge
{
private static void merge(...)
{ /* as before */ }
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort(a, aux, lo, mid);
sort(a, aux, mid+1, hi);
merge(a, aux, lo, mid, hi);
}
public static void sort(Comparable[] a)
{
aux = new Comparable[a.length];
sort(a, aux, 0, a.length - 1);
}
}
1.3 Merge sort复杂度分析
从Merge sort递归的最底层分析,即每个half都只有一个元素,将这两个元素进行merge需要进行1次比较,所有的需要
次比较,上一层是每个half有两个元素,最坏情况需要3次比较,所有的需要
次比较,依次类推,一共有lgN层递归,第k层递归需要进行
次比较,总比较次数~NlgN。
每次Merge时,一共
~ N次比较,每次比较都需要从两个half各读取一次,然后读取较小的向原数组写入,即~4N,此外每次merge要从原数组读取lo到hi,并依次写入辅助数组,即~2N,因此一共~6N,lgN层递归一共~6NlgN。
内存占用:
Proposition. Mergesort uses extra space proportional to N.
Pf. The array aux[] needs to be of size N for the last merge.
1.4 Mergesort: practical improvements
在subarray很小的时候还使用递归的方式会造成很大的overhead,因此可以在递归到 ≈ 7 items时使用insertion sort来排序,而不是一直递归到 ≈ 1 item。
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
if (hi <= lo + CUTOFF - 1)
{
Insertion.sort(a, lo, hi);
return;
}
int mid = lo + (hi - lo) / 2;
sort (a, aux, lo, mid);
sort (a, aux, mid+1, hi);
merge(a, aux, lo, mid, hi);
}
此外,当array已经排序好的时候,从左到右依次递增,之前的方式仍然要从两个half的开头进行比较,然后将较小的放到原数组,一直到左边的数组都放完,可以直接增加一个判断:
Is biggest item in first half ≤ smallest item in second half?
如果满足,则直接按顺序将两个half存到原数组即可,不需要再逐个比较。
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort (a, aux, lo, mid);
sort (a, aux, mid+1, hi);
if (!less(a[mid+1], a[mid])) return;
merge(a, aux, lo, mid, hi);
}
Eliminate the copy to the auxiliary array.
用辅助数组来存储结果
private static void merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi)
{
int i = lo, j = mid+1;
for (int k = lo; k <= hi; k++)
{
if (i > mid) aux[k] = a[j++];
else if (j > hi) aux[k] = a[i++];
else if (less(a[j], a[i])) aux[k] = a[j++];
else aux[k] = a[i++];
}
}
private static void sort(Comparable[] a, Comparable[] aux, int lo, int hi)
{
if (hi <= lo) return;
int mid = lo + (hi - lo) / 2;
sort (aux, a, lo, mid);
sort (aux, a, mid+1, hi);
merge(a, aux, lo, mid, hi);
}
2. Bottom-up mergesort
想法:
・Pass through array, merging subarrays of size 1.
・Repeat for subarrays of size 2, 4, 8, 16, ….
public class MergeBU
{
private static void merge(...)
{ /* as before */ }
public static void sort(Comparable[] a)
{
int N = a.length;
Comparable[] aux = new Comparable[N];
//sz的大小每次为之前的2倍(sz是每个subarrray的大小)
for (int sz = 1; sz < N; sz = sz+sz)
for (int lo = 0; lo < N-sz; lo += sz+sz)
merge(a, aux, lo, lo+sz-1, Math.min(lo+sz+sz-1, N-1));
}
}
注:10% slower than recursive top-down mergesort on typical systems
3. Sorting complexity
lower bound和merge sort的upper bound相同,所以merge sort是一种最优的算法。
4. Comparators
Review:
Comparable接口,使用一个类型的自然顺序进行排序
public class Date implements Comparable<Date>
{
private final int month, day, year;
public Date(int m, int d, int y)
{
month = m;
day = d;
year = y;
}
…
public int compareTo(Date that)
{
if (this.year < that.year ) return -1;
if (this.year > that.year ) return +1;
if (this.month < that.month) return -1;
if (this.month > that.month) return +1;
if (this.day < that.day ) return -1;
if (this.day > that.day ) return +1;
return 0;
}
}
Comparator接口,使用其他顺序进行排序(但必须是total order)
public interface Comparator<Key>
int compare(Key v, Key w) //compare keys v and w
4.1 Comparator interface: use with system sort
To use with Java system sort:
・Create Comparator object.
・Pass as second argument to Arrays.sort().
String[] a;
//uses natural order
Arrays.sort(a);
//uses alternate order defined by Comparator<String> object
Arrays.sort(a, String.CASE_INSENSITIVE_ORDER);
Arrays.sort(a, Collator.getInstance(new Locale("es")));
Arrays.sort(a, new BritishPhoneBookOrder());
4.2 Comparator interface: using with our sorting libraries
To support comparators in our sort implementations:
・Use Object instead of Comparable.
・Pass Comparator to sort() and less() and use it in less().
public static void sort(Object[] a, Comparator comparator)
{
int N = a.length;
for (int i = 0; i < N; i++)
for (int j = i; j > 0 && less(comparator, a[j], a[j-1]); j--)
exch(a, j, j-1);
}
private static boolean less(Comparator c, Object v, Object w)
{ return c.compare(v, w) < 0; }
private static void exch(Object[] a, int i, int j)
{ Object swap = a[i]; a[i] = a[j]; a[j] = swap; }
4.3 Comparator interface: implementing
To implement a comparator:
・Define a (nested) class that implements the Comparator interface.
・Implement the compare() method.
public class Student
{
public static final Comparator<Student> BY_NAME = new ByName();
public static final Comparator<Student> BY_SECTION = new BySection();
private final String name;
private final int section;
...
private static class ByName implements Comparator<Student>
{
public int compare(Student v, Student w)
{ return v.name.compareTo(w.name); }
}
private static class BySection implements Comparator<Student>
{
public int compare(Student v, Student w)
{ return v.section - w.section; }
}
}
4.4 Polar order
Polar order. Given a point p, order points by polar angle they make with p.
Arrays.sort(points, p.POLAR_ORDER);
public class Point2D
{
public final Comparator<Point2D> POLAR_ORDER = new PolarOrder();
private final double x, y;
...
private static int ccw(Point2D a, Point2D b, Point2D c)
{ /* as in previous lecture */ }
private class PolarOrder implements Comparator<Point2D>
{
public int compare(Point2D q1, Point2D q2)
{
double dy1 = q1.y - y;
double dy2 = q2.y - y;
if (dy1 == 0 && dy2 == 0) { ... }
else if (dy1 >= 0 && dy2 < 0) return -1;
else if (dy2 >= 0 && dy1 < 0) return +1;
else return -ccw(Point2D.this, q1, q2);
}
}
}
5. Stability
A stable sort preserves the relative order of items with equal keys.
Stable的有:
Insertion sort and mergesort (but not selection sort or shellsort).
注意:远距离exchange容易造成一个元素越过与其相等的元素,比如selection sort让当前的与后面最小的进行交换,可能就使当前的这个跑到与其相等的元素的后面去了。