快速排序-优化

随机化优化

上面提到了最常规的排序,排序,然而在面对完全有序的数组时,快速排序的效率明显降低到了O(n^2)。快速排序划分的子数组越不平衡,快排就会降低。

快速排序采用的是分而治之的思想,如果数组完全有序的话,快排分层级提升到了n,而每次都要与所有的元素进行对比。因此在面对完全有序元素时,可对快速排序进行如下优化:

随机寻找下标,不直接从最左边开始寻找。

 private static int partition(int[] arr, int l, int r) {

        //原本默认从最左边开始
        //新增下面一行,这回每次partion一般不会从最左边开始,划分的次数也不会降低到n
        swap(arr,l, new Random().nextInt((r-l)) + l );

        int v = arr[l];
        int j = l;

        for (int i = l + 1; i <= r; i++) {
            if (arr[i] < v){
                j++;
                Hepler.swap(arr,j,i);
                log.info("排序中数组{} ", arr);
            }
        }

        Hepler.swap(arr,l,j);
        log.info("排序后数组{} ", arr);
        return j;
    }

双路快速排序

  1. 定义两个下标
    《快速排序-优化》

  2. i 的位置向后扫描到(e = arr[i]) >= v,然后i停止
    《快速排序-优化》

  3. 然后j往前扫描,直到arr[j] <= v ,然后停止扫描
    《快速排序-优化》

  4. 这个时候交换arr[i]和arr[j]的位置
    《快速排序-优化》

《快速排序-优化》

  1. i再往后移动下一个位置,j继续往前移动一个位置,直到i和j重合。

当出现大量重复元素的时候,重复的元素也会分配在橙色区域和紫色区域,这样划分的区域就不会特别偏向某一方。

实现:

   public static void sort(int[] arr){
        sort(arr,0,arr.length - 1);
    }

    private static void sort(int[] arr, int l, int r) {
        if (l >= r){
            return;
        }

        int p = partition2(arr,l,r);
        sort(arr,l,p);
        sort(arr,p + 1,r);

    }

    private static int partition2(int[] arr, int l, int r) {
        Hepler.swap (arr,l,new Random().nextInt((r-l)) + l);
        int i = l + 1;
        int j = r;
        int v = arr[l];
        log.info("当前比较的v = {}",v);
        while (true){
            while (i <= r && arr[i] < v ){
                i++;
            }

            while (j >= l && arr[j] > v){
                j--;
            }

            if (i > j){
                break;
            }
            log.info("交换前数组:{}",arr);
            Hepler.swap(arr,i,j);
            log.info("找到的i = {},j={}",i,j);
            log.info("交换后数组:{}",arr);
            i++;
            j--;
        }

        Hepler.swap(arr,l,j);
        log.info("一次partion的数组:{}",arr);
        return j;

    }

三路快速排序

之前排序都是将元素分为小于v,大于v。而三路快速排序,则将元素划分为:

  • 大于v
  • 小于v
  • 等于v

流程:

  1. 定义3个会移动的索引,lt,i,gt, i指向当前要比较的元素
    《快速排序-优化》

  2. 当arr[i] == v的时候,那么e直接纳入绿色的部分
  3. 当arr[i] < v的时候,直接交换arr[i]和arr[lt+1]的元素。然后i往后移动一个元素,lt往后移动一个元素

《快速排序-优化》

  1. 当arr[i] > v的时候,只需要把arr[i]和arr[gt – 1]的元素交换位置,但是i的位置不用变,gt的元素向前移动一个元素。
    《快速排序-优化》

  2. 整个元素处理完成之后
    《快速排序-优化》

实现:


    public static void sort(int[] arr){
        quickSortThreeWays(arr,0,arr.length - 1);
    }

    private static void quickSortThreeWays(int[] arr, int l, int r){
        if (r <= l ){
            return;
        }
        int random = new Random().nextInt(r-l + 1) + l;
        Hepler.swap (arr,l,random);
        int v = arr[l];

        //当前元素的后一个元素
        // arr[0..lt] < v
        int lt = l;
        // arr[gt..r] > v
        //数组中最后一个元素
        int gt = r + 1;
        // arr[lt + 1...i] == v
        //第一个元素开始
        int i = l + 1;

        while (true){

            if (arr[i] == v){
                i++;
            }else if (arr[i] < v){
                Hepler.swap(arr,lt + 1,i);
                lt++;
                i++;
            }else if (arr[i] > v){
                Hepler.swap(arr,i,gt - 1);
                gt--;
            }
            log.info("当前i={} , lt = {},gt = {}",i,lt,gt);
            log.info("当前数组{}",arr);
            if (i >= gt){
                break;
            }
        }

        Hepler.swap(arr,l,lt );
        log.info("一次quickSort数组{}",arr);
        quickSortThreeWays(arr,l,lt - 1);
        quickSortThreeWays(arr,gt,r);

    }

最后

在《剑指Offer》上看到一句话,查找相对而言较为交单,不外乎顺序查找,二分查找,哈希查找和二叉树排序查找,在面试的时候不管使用循环还是递归,面试官都期待应聘者能信手沾来的写出完整的二分查找代码,否则连继续面试的兴趣都没有。

排序是算法中的基础,加油。

点赞