普林斯顿算法课Part 1 Week 3 Mergesort

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Cyril__Li/article/details/79892816

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次比较,所有的需要 N 2 × 1 次比较,上一层是每个half有两个元素,最坏情况需要3次比较,所有的需要 N 4 × 3 次比较,依次类推,一共有lgN层递归,第k层递归需要进行 N 2 k ( 2 k 1 ) 次比较,总比较次数~NlgN。
每次Merge时,一共 N 2 k ( 2 k 1 ) ~ 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让当前的与后面最小的进行交换,可能就使当前的这个跑到与其相等的元素的后面去了。

猜你喜欢

转载自blog.csdn.net/Cyril__Li/article/details/79892816