排序——快速排序

本文详细介绍了快速排序算法的思路、实现过程及图示,包括输入输出、递归分组和分区步骤。快速排序是一种原位排序,通过递归将数组划分为较小和较大元素两部分,以基准元素为界进行排序。与归并排序相比,快速排序在分组过程中逐步排序,归并操作简单。
摘要由CSDN通过智能技术生成

源码在这里下载


前言

我们通常习惯把快速排序和归并排序放在一起对比,它们有许多相似之处,也有不同的地方。学习完之后,你会对分治以及递归有更深的理解。


一、算法思路

下面是快速排序的思路:
(1)如果有一个或者更少的数字需要排序,则不做任何事情。
(2)否则,把区域分成“较小”数字和“较大”数字两部分,把较小的数字移到左边,把较大的数字移到右边。递归地对每个区域进行排序。整个数组现在是有序的。

二、算法实现

    /** Arrange the numbers in data from smallest to largest. */
    public static void quickSort(int[] data){
        quickSortHelper(data, 0, data.length-1);
    }
    
    /**
     * Arrange the numbers in data between indices bottom and top,
     * inclusive, from smallest to largest.
     */
    protected static void quickSortHelper(int[] data, int bottom, int top){
        if (bottom <= top){
            int midpoint = partition(data,bottom,top);
            quickSortHelper(data,bottom, midpoint-1);
            quickSortHelper(data, midpoint + 1, top);
        }
    }
    
    /**
     * Choose one element of data in the region between bottom and top, 
     * inclusive, as the pivot. Arrange the numbers so that those less 
     * than or equal to the pivot are to the left of it and those
     * greater than the pivot are to the right of it. Return the final
     * position of the pivot.
     */
    protected static int partition(int[] data, int bottom, int top){
        int pivot = data[top];
        int firstAfterSmall = bottom;
        for (int i = bottom; i < top; i++){
            if (data[i] < pivot){
                swap(data,i,firstAfterSmall);
                firstAfterSmall++;
            }
        }
        swap(data,firstAfterSmall,top);
        return firstAfterSmall;
    }
    
    /** Swap the element of data at indices i and j. */
    protected static void swap(int[] data, int i, int j){
        int temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }
    

三、算法图示

快速排序图示

四、算法步骤

1.输入输出

输入待排序数组
输出因为快速排序是一个原位排序的方法,直接在原数组操作,所以不需要返回值。

    public static void quickSort(int[] data){
        quickSortHelper(data, 0, data.length-1);
    }

2.递归分组

调用一个Helper方法,实现数组的分组。值得注意的是,这里的分组应该与下面的分区区别开。分组是每次以分区方法partition()得到的midpoint为界,将数组分成两个子序列。分区是在当前序列内,将元素划分为较小区域和较大区域,以pivot为界。还有就是,虽然分组的分割点叫做midpoint,但是却不是在正中间的位置,这个位置是根据partition得到的基准元素的最终位置来确定的,不要看到midpoint就以为是(bottom+top)/2

    protected static void quickSortHelper(int[] data, int bottom, int top){
        if (bottom <= top){
            int midpoint = partition(data, bottom, top);
            quickSortHelper(data, bottom, midpoint-1);
            quickSortHelper(data, midpoint + 1, top);
        }
    }

该处使用的url网络请求的数据。

3.分区

分区方法的作用是在当前数组内,将元素划分为较小区域和较大区域,以pivot为界。那么如何确认pivot的位置呢。这里我们使用当前数组中下标最大的一个元素作为基准pivot,那么可不可以选择其他位置呢,这个留给读者思考。我们的目的是完成元素的分区,所以选择基准的位置也要考虑后续操作是否方便。firstAfterSmall顾名思义,较小分区后的第一个元素(的下标)。所以它的左侧始终是已知较小区域。循环的作用是把所有比基准元素小的元素通过swap()方法依次交换到当前数组的左侧。循环结束之后,firstAfterSmall左侧都是比pivot小的元素。此时,交换firstAfterSmall所在位置的元素和pivot(即data[top])。最终得到的结果就是:基准元素左侧的元素都小于它,右侧的元素都大于它。换个思路来想,我们相当于确定了基准元素在数组中的位置。

protected static int partition(int[] data, int bottom, int top){
        int pivot = data[top];
        int firstAfterSmall = bottom;
        for (int i = bottom; i < top; i++){
            if (data[i] < pivot){
                swap(data,i,firstAfterSmall);
                firstAfterSmall++;
            }
        }
        swap(data,firstAfterSmall,top);
        return firstAfterSmall;
    }
    
    protected static void swap(int[] data, int i, int j){
        int temp = data[i];
        data[i] = data[j];
        data[j] = temp;
    }

总结

最后,简单对比一下归并排序与快速排序。虽然都是采用了递归方法,使用分治的思想完成排序。但是归并排序是先分组,在归并的过程中完成排序,也就是说归并时的工作量会比较大。而快速排序也是先分组,但是是在分组的时候就逐步排序,归并的操作就十分简单了。当然还有诸如稳定性等问题,后面我们再深入讨论。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值