Java 快速排序

整一下老早就听说过的,但是却没有深入了解过的排序算法。
首先,排序算法分为两大类:

  • 比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破O(nlogn),因此也称为非线性时间比较类排序。
  • 非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。

给一张图,内容是十大经典排序算法的时间复杂度,空间复杂度以及稳定性。
稳定:如果a原本在b前面,而a=b,排序之后a仍然在b的前面。
不稳定:如果a原本在b的前面,而a=b,排序之后 a 可能会出现在 b 的后面。

image

快速排序的基本思想:
通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序。
排序流程如下:
(1)首先设定一个分界值,通过该分界值将数组分成左右两部分。
(2)将大于或等于分界值的数据集中到数组右边,小于分界值的数据集中到数组的左边。此时,左边部分中各元素都小于或等于分界值,而右边部分中各元素都大于或等于分界值。
(3)然后,左边和右边的数据可以独立排序。对于左侧的数组数据,又可以取一个分界值,将该部分数据分成左右两部分,同样在左边放置较小值,右边放置较大值。右侧的数组数据也可以做类似处理。
(4)重复上述过程,可以看出,这是一个递归定义。通过递归将左侧部分排好序后,再递归排好右侧部分的顺序。当左、右两个部分各数据排序完成后,整个数组的排序也就完成了。
具体解析参考如下代码(从小到大):

    public static void main() {
        // 对0->len-1进行排序
        qSS(arr,0,arr.length-1);
    }

    private void qSS(int[] arr,int left,int right) {
        // 左边界需要小于右边届
        if (left<right) {
            // 获取排序完成的分界值所在的位置
            int partitionIndex = qS(arr,left,right);
            // 对分界值左边的内容进行排序
            qSS(arr,0,partitionIndex-1);
            // 对分界值右边的内容进行排序
            qSS(arr,partitionIndex+1,right);
        }
    }

    // 单次排序
    private int qS(int[] arr,int left,int right) {
        // 取left作为分界值,index为交换坐标,index初始化为left右边的第一个值
        // ex: {6[left],5[index],4,9,8,7,3,2,1};
        int index = left +1;
        // 从i=index开始循环
        for (int i=index;i<=right;i++) {
            // 如果arr[i]小于arr[left],将arr[i]与arr[index]交换
            // 这样比left小的值将移动到left的右边
            // 同时index向右移动一位,此时index左侧的内容都应该<=arr[left]
            if (arr[i]<arr[left]) {
                swap(arr,i,index);
                index++;
            }
        }
        // 以上全部交换完毕之后,将left与index-1交换
        // 得到以index-1位置为分界的数组
        // {6[left],5,4,3,2,1,9[index],8,7} ==>
        // {1[left],5,4,3,2,6,9[index],8,7}
        swap(arr, left,index-1);
        return index-1;
    }


    private void swap(int[] arr,int x,int y) {
        if (arr[x]==arr[y]) return;
        int temp = arr[x];
        arr[x] = arr[y];
        arr[y] = temp;
    }

写法二(从大到小):

    private void quickSort(int[] arr,int left,int right) {
        if (left>right) {
            return;
        }
        // 左边界
        int i = left;
        // 右边界
        int j = right;
        // 以左边界为届值
        int cur = arr[left];
        // 循环
        while (i<j) {
            // 从右边开始,找到一个大于cur的值停下来
            while (cur<=arr[j] && i<j) {
                j--;
            }
            // 从左边开始,找到一个小于cur的值停下来
            while (cur>=arr[i] && i<j) {
                i++;
            }
            // 满足条件则交换两值
            if (i<j) {
                int t = arr[j];
                arr[j] = arr[i];
                arr[i] = t;
            }
            // ex : {6[cur],5[i],4,9,8,7,3,2,1[j]} ==>
            // ex : {6[cur],5[i],4,9,8,7[j],3,2,1} ==>
            // ex : {6[cur],7[i],4,9,8,5[j],3,2,1}
        }

        // ex : {6[cur],7,8,9[i][j],4,5,3,2,1}
        // 将arr[i]与arr[left]互换
        arr[left] = arr[i];
        // cur就是left的temp值
        arr[i] = cur;
        // 排序左半部分
        quickSort(arr,left,j-1);
        // 排序右半部分
        quickSort(arr,j+1,right);
    }

上面源码的细节都添加注释,接下来是快速排序的运用:
数组中的第K个最大元素,话不多说直接上源码。

    public int findKthLargest(int[] nums, int k) {
        // 左边界
        int left = 0 ;
        // 右边届
        int right = nums.length-1;
        // 总长度(n-k所在的位置即为第K大)
        int n = nums.length;
        while (true){
            // pos依旧是基准位置,进行快排(使用方法二)
            int pos = quickSort(nums,left,right);
            // 如果pos的位置刚刚好是n-k,直接返回
            if (pos==n-k) {
                return nums[pos];
            // 如果pos在k右侧,则右边届移动到pos-1
            } else if (pos>n-k) {
                r = pos-1;
            // 如果 pos在k左侧,则左边界移动到pos+1
            } else {
                l = pos+1;
            }
        }
    }
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容