各种快速排序算法

在内部排序算法中比较常用的排序算法应该算快速排序了。时间复杂度为N*lgN。然而快排算法的实现又有很多的版本。下面总结一下。

一、常规快排算法(以数组中一个元素作为旋转点)

 // quicksort the subarray from a[lo] to a[hi]
    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);
        assert isSorted(a, lo, hi);
    }


划分方法如下

// partition the subarray a[lo .. hi] by returning an index j
    // so that a[lo .. j-1] <= a[j] <= a[j+1 .. hi]
    private static int partition(Comparable[] a, int lo, int hi) {
        int i = lo;
        int j = hi + 1;
        Comparable v = a[lo];
        while (true) { 

            // find item on lo to swap
            while (less(a[++i], v))
                if (i == hi) break;

            // find item on hi to swap
            while (less(v, a[--j]))
                if (j == lo) break;      // redundant since a[lo] acts as sentinel

            // check if pointers cross
            if (i >= j) break;

            exch(a, i, j);
        }

        // put v = a[j] into position
        exch(a, lo, j);

        // with a[lo .. j-1] <= a[j] <= a[j+1 .. hi]
        return j;
    }


其中,划分方法中的选择点的选择对算法的性能影响比较大。基于这个原因,有些改进算法,有的选择数组的中间项作为旋转点,但这样的改进等于没改进,因为选第一个和选中间那个效果是一样的,觉得是一种自欺欺人的方法。有一种比较合理的改进是,先用shuffle算法(洗牌算法)对原始数组进行处理,使得数组中的各项值均匀地随机排列,然后再用常规快排算法。

// quicksort the array
    public static void sort(Comparable[] a) {
        StdRandom.shuffle(a);
        sort(a, 0, a.length - 1);
    }

    // quicksort the subarray from a[lo] to a[hi]
    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);
        assert isSorted(a, lo, hi);
    }

还有一种改进旋转点的方法是采用三者取中划分。就是从数组选出三个元素,使用这三个元素的中间值的那个元素作为划分点。

 

二、三路划分快速排序算法。改算法将数组划分为3部分,比划分元素小的元素,与划分元素相等的元素,比划分元素大的元素。然后递归处理比划分元素小的元素和比划分元素大的元素。

// quicksort the subarray a[lo .. hi] using 3-way partitioning
    private static void sort(Comparable[] a, int lo, int hi) { 
        if (hi <= lo) return;
        int lt = lo, gt = hi;
        Comparable v = a[lo];
        int i = 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++;
        }

        // a[lo..lt-1] < v = a[lt..gt] < a[gt+1..hi]. 
        sort(a, lo, lt-1);
        sort(a, gt+1, hi);
        assert isSorted(a, lo, hi);
    }


三、快排的综合改进版。当递归调用快速排序算法时,处理的数组的规模会越来越小,当小的一定程度时,采用比较简单的插入排序算法会比快速排序更有优势;同时采用前面讲到的三者取中划分方法和三路快排。

private static void sort(Comparable[] a, int lo, int hi) { 
        int N = hi - lo + 1;

        // cutoff to insertion sort
        if (N <= CUTOFF) {
            insertionSort(a, lo, hi);
            return;
        }

        // use median-of-3 as partitioning element
        else if (N <= 40) {
            int m = median3(a, lo, lo + N/2, hi);
            exch(a, m, lo);
        }

        // use Tukey ninther as partitioning element
        else  {
            int eps = N/8;
            int mid = lo + N/2;
            int m1 = median3(a, lo, lo + eps, lo + eps + eps);
            int m2 = median3(a, mid - eps, mid, mid + eps);
            int m3 = median3(a, hi - eps - eps, hi - eps, hi); 
            int ninther = median3(a, m1, m2, m3);
            exch(a, ninther, lo);
        }

        // Bentley-McIlroy 3-way partitioning
        int i = lo, j = hi+1;
        int p = lo, q = hi+1;
        while (true) {
            Comparable v = a[lo];
            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);
            if (eq(a[i], v)) exch(a, ++p, i);
            if (eq(a[j], v)) exch(a, --q, j);
        }
        exch(a, lo, j);

        i = j + 1;
        j = j - 1;
        for (int k = lo+1; k <= p; k++) exch(a, k, j--);
        for (int k = hi  ; k >= q; k--) exch(a, k, i++);

        sort(a, lo, j);
        sort(a, i, hi);
    }


    // sort from a[lo] to a[hi] using insertion sort
    private static void insertionSort(Comparable[] a, int lo, int hi) {
        for (int i = lo; i <= hi; i++)
            for (int j = i; j > lo && less(a[j], a[j-1]); j--)
                exch(a, j, j-1);
    }


    // return the index of the median element among a[i], a[j], and a[k]
    private static int median3(Comparable[] a, int i, int j, int k) {
        return (less(a[i], a[j]) ?
               (less(a[j], a[k]) ? j : less(a[i], a[k]) ? k : i) :
               (less(a[k], a[j]) ? j : less(a[k], a[i]) ? k : i));
    }


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
内排序是指在排序过程中所有数据都能被放在内存中完成排序。常见的内排序算法包括以下几种: 1. 冒泡排序(Bubble Sort):重复走访要排序的数列,每次比较相邻两个元素,如果它们的顺序错误就把它们交换过来。 2. 选择排序(Selection Sort):每次从待排序的数据元素中选出最小(或最大)的一个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。 3. 插入排序(Insertion Sort):将一个记录插入到已经排好序的有序表中,从而得到一个新的、记录数增1的有序表。 4. 希尔排序(Shell Sort):将待排序的数组元素按下标的一定增量分组,对每组使用插入排序算法进行排序,随着增量逐渐减少,每组包含的元素越来越多,当增量减至1时,整个数组恰被分成一组,最后进行插入排序。 5. 快速排序(Quick Sort):通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,然后分别对这两部分继续进行排序。 6. 归并排序(Merge Sort):将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。 7. 堆排序(Heap Sort):利用堆这种数据结构所设计的一种排序算法。堆排序的过程就是将待排序的序列构造成一个堆,根据堆的性质,最大(或最小)的元素就是堆顶的根节点,将其与堆末尾元素交换,然后将剩余的元素重新构造成一个堆,重复此步骤直至整个序列有序。 8. 计数排序(Counting Sort):根据待排序序列中每个元素在序列中出现的次数,统计出每个元素在最终有序序列中的位置,进而将待排序序列排序。 9. 桶排序(Bucket Sort):将待排序序列中的元素按一定规则分配到不同的桶中,对每个桶中的元素进行排序,最后将所有桶中的元素按顺序合并成有序序列。 10. 基数排序(Radix Sort):将待排序元素按照位数划分为不同的数字,然后按照每个数字的大小依次进行排序,直至所有位数排序完成,最终得到一个有序序列。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值