• 排序之 快速排序


    采用算法导论上的实现方式,用java实现。

    快排算法核心的部分便是partition过程,这里的partition采取最后一个元素作为pivot,i和j两个指针都从头向后扫描,如下图所示,数组被分为4个部分。

    算法执行的过程:

    代码实现:包括快速排序寻找第K大元素洗牌算法

    import java.util.Arrays;
    import java.util.Random;
    
    public class MySort {
    
        /**
         * 快速排序
         * 
         * @param a
         */
        public static void quickSort(int[] a) {
            qSort(a, 0, a.length - 1);
        }
    
        private static void qSort(int[] a, int p, int r) {
            if (p < r) {// 递归算法不要忘记了出口
                int q = partition(a, p, r);
                qSort(a, p, q - 1);
                qSort(a, q + 1, r);
            }
        }
    
        private static int partition(int[] a, int p, int r) {
            int x = a[r];
            int i = p - 1;
            int j = p;
            for (j = p; j < r; j++) {
                if (a[j] <= x) {
                    i++;
                    swap(a, i, j);
                }
            }
            swap(a, i + 1, r);
            return i + 1;
        }
    
        private static void swap(int[] a, int i, int j) {
            if (i != j) {
                int tmp = a[i];
                a[i] = a[j];
                a[j] = tmp;
            }
    
        }
    
        /**
         * 返回第k大的元素。
         * 
         * @param a
         * @param k
         * @return
         */
        public static int selectKSmallest(int[] a, int k) {
            if (k >= a.length || k < 0)
                return -1;
            qSelect(a, k, 0, a.length - 1);
            return a[k];
    
        }
    
        private static void qSelect(int[] a, int k, int p, int r) {
            if (p < r) {
                int q = partition(a, p, r);
                if (q > k)
                    qSelect(a, k, p, q - 1);
                else if (q < k)
                    qSelect(a, k, q + 1, r);
                else
                    return;
            }
    
        }
    
        /**
         * 洗牌算法
         * 
         * @param a
         */
        public static void shuffle(int[] a) {
            for (int i = 0; i < a.length; i++) {
                int k = new Random().nextInt(i + 1);// return a random value in
                                                    // [0,i].
                int tmp = a[k];
                a[k] = a[i];
                a[i] = tmp;
            }
        }
    
        public static void main(String[] args) {
            int[] a = { 2, 8, 7, 1, 3, 5, 6, 4 };
            System.out.println("before sorting:" + Arrays.toString(a));
            quickSort(a);
            System.out.println("after sortint:" + Arrays.toString(a));
    
            shuffle(a);
            System.out.println("after shuffling:" + Arrays.toString(a));
    
            System.out.println(selectKSmallest(a, 3));
    
        }
    
    }

    正确性证明:

    双指针向内的快速排序:   Algorithms里讲的很好,看这里

    import java.util.Arrays;
    
    public class test {
    
        public static void quickSort2(int[] a) {
            qSort(a, 0, a.length - 1);
        }
    
        /**
         * 注意1:递归终止条件 l>=r 
         * 注意2:i和j初始位置,用do while保证至少有一次变化,不会原地不动
         * 注意3:j最终的位置是要和l交换的位置。
         * 注意4:对于跟pivot相等的情况,应该停住两个指针并交换,虽然交换次数增多,但是保证partition更平均,复杂度更低。
         */
        private static void qSort(int[] a, int l, int r) {
            if (l >= r)
                return;
            int t = a[l];
            int i = l, j = r + 1;
            while (true) {
                do {
                    i++;
                } while (i <= r && a[i] < t);
    
                do {
                    j--;
                } while (a[j] > t);
                if (i > j)
                    break;
                swap(a, i, j);
    
            }
            swap(a, l, j);
            qSort(a, l, j - 1);
            qSort(a, j + 1, r);
        }
    
        private static void swap(int[] a, int i, int j) {
            int tmp = a[i];
            a[i] = a[j];
            a[j] = tmp;
        }
    
        public static void main(String[] args) {
            int[] a = { 1, 1, 1, 1, 1 };
            System.out.println(Arrays.toString(a));
            quickSort2(a);
            System.out.println(Arrays.toString(a));
        }
    }

    快速排序细节及优化:

    1. pivot随机化:不一定每次都选第一个为pivot, 可以先做 swap(a[0], a[rand(l,r)]);或者选取三个样本中的中位数作为pivot

    2. 两个指针移动的时候对于跟pivot相等的元素,应该停住交换,而不是跨过。(虽然增加了交换时间,但是保证了极端情况,比如元素全部相等情况下,partition分区极不均匀的情况。)

    3. r-l 小于一定的cutoff之后,选用insertion sort等方法。

    4. 3-way-partation。:当数组有大量重复元素时,这种优化复杂度甚至可以降到线性。

  • 相关阅读:
    (转)游戏设备的三大未来趋势
    (转)零基础学习 Hadoop 该如何下手?
    (转)如何系统地学习数据挖掘?
    (转)大数据最核心的价值是什么?
    (转)面向对象编程的弊端是什么?
    (转)处理器架构、指令集和汇编语言,三者有何关系?
    (转)游戏界人士如何看待《征途》这款游戏?
    (转)想从事游戏开发,1 年内能精通 C++ 吗,还需要学习什么?
    (转)数据库老兵:NewSQL才是未来
    (转)神舟飞船上的计算机使用什么操作系统,为什么是自研发不是 Linux?
  • 原文地址:https://www.cnblogs.com/jdflyfly/p/3897331.html
Copyright © 2020-2023  润新知