算法|快速排序

算法实现请移步这里

前面已经陆续有:选择排序 ,冒泡排序,插入排序,希尔排序,归并排序

可以看到,是按照排序算法的性能来罗列的.

快速排序和归并排序一样,也是"分治思想"的经典应用

快速排序能很好的适用于各种各种的随机输入,并且大多数情况下,都要优于以上其他算法,并且只需要有限的辅助数组(归并排序需要额外的等量的内存空间),它的内存换也是相当的简单.

上面提到它跟归并排序一样,也是使用"分治思想"来进行算法设计,所以,快速排序也是递归实现的排序算法

算法描述:

快速排序将数组递归的分成两半,然后分别对他们排序.


" shuffle  " 是在排序前对输入进行随机排序.
选定第一个元素K(a[0])作为切分元素(用来将数组切分为两半),将数组进行切分成三部分
  1. 左边(a[low]...a[j-1]),
  2. 切分元素,a[j]
  3. 右边(a[j+1]....a[height])
这三组子数组之间的关系为,左边的元素都不大于a[j],右边的元素都不小于a[j]

然后对左边进行相同的切分,再对右边进行相同的切分,整个过程是递归的.


切分算法的结果保证

a[j]一定在正确的位置j

a[low]...a[j-1]都不大于a[j]

a[j+1]....a[height]都不小于a[j]

切分算法的过程是这样的:

为了获取正确的a[j],我们每次都选择a[low](待切分数组的第一个元素)作为位置j的切分元素,然后分别从数组的两端开始遍历:从左到右依次遍历数组元素,直到a[i] >=a[low]时停止,从右到左依次遍历,直到a[j]<=a[low]时停止,然后交换a[i]和a[j]元素,然后继续前面的遍历,直到数组结束.最终结束时的j就是切分元素a[low]的正确位置.遍历的过程为:


切分的算法实现为:

private static int partition(int[] array, int low, int height) {
        //partition the array to array[low]...a[j-1], a[j], a[j+1]...a[height]
        int i = low;
        int j = height + 1;

        int value = array[low];// the partition value

        //exchange the left and right along with scan
        int temp;
        while (true) {
            while (array[++i] <= value) if (i >= height) break;
            while (array[--j] >= value) if (j <= low) break;
            if (i >= j) break;

            //exchange the array[i] and array[j]
            temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }

        //exchange the array[low] and array[j]
        temp = array[low];
        array[low] = array[j];
        array[j] = temp;

        return j;
    }

具体的切分排序过程为:


当我们将数组经过一次切分后,就可以使用递归的方式,对剩下的数组进行相同的切分排序,最终直到整个数组排序

其实每次切分后都能保证a[j]左边的元素都不大于a[j], a[j]右边的元素都不小a[j],那么只需要对剩下的数组继续使用切分算法就好了,但是被切分的两半数组内部是没有顺序的,最终数组在递归的切分下,最终还是变成有序数组了?怎么做到的呢?本质与归并排序类似,我们递归排序的中,我们总是递归的将数组进行对半分,直到子数组的长度变成1时,才使用merge方法进行元素的比较,而且这种比较是基于子数组本身有序来保证的,只有当子数组长度为1时,才保证了子数组自身的有序特征,这个时候才能开始merge(归并),只有基于这个基础才能保证归并排序的正确性,所以归并排序不管是自顶向下,还是自底向上的归并,最终调用merge之前,子数组必须有序!同样的,每次切分后,被切分成的子数组本身是没有顺序的,他们只是不小于或者不大于a[j],但是当切分的数组长度也为1时,只剩下两个元素的比较,这个时候就变成有序的,从这个点开始往回溯,所有的有序数组组合成更大的有序数组!

整个排序过程为:


红色元素是切分元素!

整个算法的实现为:

public class QuickSort {

    public static void main(String[] args) {

        int[] nums = RandomSequence.retrieveSequence();
        long previousTime = System.currentTimeMillis();
        sort(nums, 0, nums.length - 1);
        System.out.println("Quick sort cost " + (System.currentTimeMillis() - previousTime));

    }

    public static void sort(int[] array, int low, int height) {
        if (low >= height) return;

        int j = partition(array, low, height);

        sort(array, low, j - 1);

        sort(array, j + 1, height);
    }


    private static int partition(int[] array, int low, int height) {
        //partition the array to array[low]...a[j-1], a[j], a[j+1]...a[height]
        int i = low;
        int j = height + 1;

        int value = array[low];// the partition value

        //exchange the left and right along with scan
        int temp;
        while (true) {
            while (array[++i] <= value) if (i >= height) break;
            while (array[--j] >= value) if (j <= low) break;
            if (i >= j) break;

            //exchange the array[i] and array[j]
            temp = array[i];
            array[i] = array[j];
            array[j] = temp;
        }

        //exchange the array[low] and array[j]
        temp = array[low];
        array[low] = array[j];
        array[j] = temp;

        return j;
    }

}

可以看出,sort(int[] array, int low, int height) 的递归调用仅仅是为了安排合理的

partition(int[] array, int low, int height)方法调用

递归调用在小规模数组上是很不利的,会过多的在小规模数组上进行递归调用,所以我们像在归并排序中的优化那样,对小规模的数组不再使用递归,而是使用插入排序,例如:

  public static void sort(int[] array, int low, int height) {
//        if (low >= height) return;
        if (low + 10 >= height) {// fast then original quick sort about 200ms at size of 1<<24
            InsertSort.sort(array, low, height);
            return;
        }

        int j = partition(array, low, height);

        sort(array, low, j - 1);

        sort(array, j + 1, height);
    }

在千万级别的数组规模下,可以提升200-300ms的时间

有时间再介绍三向切分法,应对大量重复元素的情况下,可以极大的提升排序性能,后面会做排序性能的检测


算法复杂度分析


完成时间-2018-04-28(由于项目忙,可能会晚于这个时间点,但是一定会尽量完成!)

下一篇:堆排序

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值