快速排序

快速排序

快速排序最坏情况时间复杂度为θ(n^2),虽然最坏情况时间复杂度很差,但快速排序通常是实际排序应用中最好的选择,其平均性能很好,期望时间复杂度是θ(nlgn)

快速排序算法描述

与归并排序类似,快速排序使用分治思想进行排序

分解

数组A[p…r]被划分为两个子数组A[p…q-1]和A[q+1…r],使得A[p…q-1]中的每一个元素都小于等于A[q],而A[q]也小于等于A[q+1…r]中的每个元素,计算下标q也是划分过程的一部分

解决

通过递归调用快速排序,对子数组A[p…q-1]和A[q+1…r]进行排序

合并

因为子数组是原址排序,不需要合并,数组A[p…r]已经有序

快速排序伪代码

QUICKSORT(A, p, r)
1   if p < r
2       q = PARTITION(A, p, r)
3       QUICKSORT(A, p, q-1)
4       QUICKSORT(A, q+1, r)

数组划分伪代码

PARTITION总是选择一个x = A[r]作为主元,并围绕它来划分子数组A[p…r],随着程序执行,数组被划分为4个区域。在第3~6行for循环每一轮迭代的开始,每个区域满足一定的性质

  • 若p ≤ k ≤ i,则A[k] ≤ x

  • 若i + 1 ≤ k ≤ j - 1,则A[k] > x

  • 若k = r, 则A[k] = x

PARTITION(A, p, r)
1   x = A[r]
2   i = p - 1
3   for j = p to r - 1
4       if A[j] ≤ x
5           i = i +  1
6           exchange A[i] with A[j]
7   exhange A[i + 1] with A[r]
8   return i + 1

快速排序性能

快速排序运行时间依赖于划分是否平衡,平衡与否又取决于所选取的划分元素。如果划分是平衡的,快速排序算法性能与归并排序一样。如果划分不平衡,则与插入排序性能接近

最坏情况划分

当划分产生的两个子数组分别包含n-1个元素和0个元素,快速排序最坏情况产生,如果每次递归调用都出现这种最坏情况,则最坏情况的时间复杂度为θ(n^2)

最好情况划分

当划分产生的两个子数组规模都不大于n/2,其中一个子问题的规模为⌊n/2⌋,另一个子问题规模为⌈n/2⌉ - 1,如果每次迭代都出现这种最好情况划分。则最好情况时间复杂度为θ(nlgn)

平衡的划分

快速排序的平均运行时间更接近于最好情况,而不是最坏情况。原因在于划分的平衡性与描述运行时间递归式的关系

假设划分算法总是产生9:1的划分,快速排序时间复杂度递归式为:

        T(n) = T(9n/10) + T(n/10) + cn

整棵递归数深度为 log ⁡ 10 / 9 n \log_{10/9} {n} log10/9n,在深度为 log ⁡ 10 n \log_{10} {n} log10n之前每一层代价为cn,之后每一层代价至多为cn,因此快速排序总代价为O(nlgn),事实上任何常数比例的划分,都会产生深度为θ(lgn)的递归树,每一层时间代价都是O(n),算法运行时间总是O(nlgn)

快速排序随机化版本

为了获得更好的平均性能,在主元选取时,我们使用随机抽样。从子数组A[p…r]中随机选择一个元素作为主元,并将A[r]与随机选出的主元做交换

RANDOMIZED-PARTITION(A, p, r)
1   i = RANDOM(p, r)
2   exchange A[r] with A[i]
3   return PARTITION(A, p, r)

Java实现

package com.aim.algorithm.sort.impl;

import com.aim.algorithm.sort.Sort;

public class QuickSort implements Sort {

    @Override
    public int[] sort(int[] input) {
        quickSort(input, 0, input.length - 1);
        return input;
    }

    private void quickSort(int[] input, int start, int end){
        if(start < end){
            int q = randomPartition(input, start, end);
            quickSort(input, start, q - 1);
            quickSort(input, q + 1, end);
        }
    }

    private int randomPartition(int[] input, int start, int end){
        int i = (int) (Math.random() * (end - start) + start);

        int changEnd = input[end];
        input[end] = input[i];
        input[i] = changEnd;
        return partition(input, start, end);
    }

    private int partition(int[] input, int start, int end) {
        int x = input[end];
        int i = start - 1;
        for(int j = start; j < end; j++){
            if(input[j] < x){
                ++i;
                int changValueI = input[i];
                input[i] = input[j];
                input[j] = changValueI;
            }
        }
        int changEnd = input[end];
        input[end] = input[i + 1];
        input[i + 1] = changEnd;
        return i + 1;
    }
}

// 测试方法
public static void quickSort() throws IOException {
    Scanner scan = new Scanner(System.in);
    System.out.println("请输入要排序数组");
    String arrayStr = scan.nextLine();
    while (Strings.isBlank(arrayStr)) {
        System.out.println("请输入要排序数组");
        arrayStr = scan.nextLine();
    }
    String[] arrays = arrayStr.split(" ");
    int[] input =  Arrays.stream(arrays).mapToInt(array -> Integer.valueOf(array)).toArray();

    int[] result = quickSort.sort(input);
    Arrays.stream(result).forEach(rsl -> System.out.println(rsl));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值