从最初的感动开始--JAVA算法【2】--归并和快排

来源:互联网 发布:淘宝的投诉电话是多少 编辑:程序博客网 时间:2024/06/06 01:31

花了半个礼拜火速重温了JAVA基础语法后,终于来到了算法的第一个高潮…排序

首先奉上归并排序的代码:

class Merge {    private static Comparable[] aux;    public static void merge(Comparable[] a,int lo,int mid,int hi){        //merge the a[lo..mid] and a[mid+1..hi]        int i= lo, j = mid +1;        //copy the array        for (int k = lo; k<=hi; k++){            aux[k] = a[k];        }        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++];        }    }    private static boolean less(Comparable comparable, Comparable comparable0) {        return comparable.compareTo(comparable0)<0;    }    public static void sort(Comparable[] a){        aux = new Comparable[a.length];        sort(a,0,a.length-1);    }    private static void sort(Comparable[] a, int lo, int hi) {        if (hi<=lo) return;        int mid = lo+ (hi-lo)/2;        sort(a, lo, mid);        sort(a, mid+1, hi);        merge(a, lo, mid, hi);    }}

归并排序的高潮在于分而治之,这是一种贯穿程序算法设计始终的思想。
Merge()函数中,分别构造两个新指针,一个从数组头部开始(i),一个从数组的中间开始(j),分别和k元素比较,那边小就动哪边,一直到循环退出。

接下来是经典的原始快排:

class Quick {    public static void sort(Comparable[] a){        StdRandom.shuffle(a);        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) {        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);        return j;    }    private static boolean less(Comparable comparable, Comparable comparable0) {        return comparable.compareTo(comparable0)<0;    }    private static void exch(Comparable[] a, int i, int j) {        Comparable t = a[i]; a[i] = a[j]; a[j] = t;    }}

快排的思想在于…好吧,同样是分而治之,这点从第二个sort()函数的迭代结构便可知晓。
和归并相比,其优势在于其潜在的元素交换更少,从而更节省资源和时间。此外,也不需要再构建一个数组做中间变量(归并中的aux全局变量的功能)。

值得注意的是,这个快排的代码已经做了优化。
在做切分的partition()函数中,左扫描的终止条件为 !less(a[++i],v), 即左边大于等于时v时,终止左扫描开始右扫描。 而右扫描的终止条件为 !less(v, a[–j]),即右边小于等于v时,终止右扫描,如果在此时左右扫描都还没到头时,交换左右扫描停止时所指向的位置(exch(a, i, j))。
注意,这里左右扫描停止的条件,包含“等于”。如果这个条件去掉,只有当左边大于v,右边小于v时,才会停止扫描,那么和v相等的元素在一次切分后,仍然散落在数组各处,在后续的切分过程中,这些值仍将一次次地执行比较和位移,极大浪费了之前的比较操作所付出的成本。

然而即使已经做出了改进,与v值相等的元素,依然会被做为子数组,在切分完成后,被划入子数组中,再次参与到下一步的排序。为了避免这点,出现了三向快排:

class Quick3way {    private static void sort(Comparable[] a, int lo, int hi){        if (hi <= lo) return;        int lt = lo, i = lo+1, gt = hi;        Comparable v = a[lo];        while (i <= gt){            int cmp = a[i].compareTo(v);            if      (cmp<0) exch(a, lt++, i++);            else if (cmp>0) exch(a, i, gt--);            else    i++;        }        sort(a, lo, lt-1);   //skip the elements, which has the same value of v        sort(a, gt+1, hi);   //that will save lots of time       }    public static void sort(Comparable[] a){        sort(a,0,a.length-1);    }    private static void exch(Comparable[] a, int i, int j) {        Comparable t = a[i]; a[i] = a[j]; a[j] = t;    }}

三向快排多了一个指针,它标记了与v值相等的元素,这些元素在本次切分完成后,不再参与到下一次的排序,从而大大缩减了后续的操作步骤。

理论上,没有任何基于比较的排序算法,能够让比较次数少于N*lgN,然而三向快排利用了先期比较时所获得的信息,从而在对存在大量重复数据进行排序时,能以更少的比较步骤完成排序工作。

0 0
原创粉丝点击