快排(双路快排 )

对于快速排序的理解

  • 基本思路:快速排序每一次都排定一个元素(这个元素呆在了它最终应该呆的位置),然后递归地去排它左边的部分和右边的部分,依次进行下去,直到数组有序

第一版代码

import java.util.Random;
class Solution {
    
    private Random random = new Random(System.currentTimeMillis());

    public int[] sortArray(int[] nums) {
        quickSort(nums, 0, nums.length - 1);
        return nums;
    }

    public void quickSort(int[] nums, int left, int right) {
        if(left >= right) return; // 终止条件,left=right的时候只有一个元素不需要排序
        int pivotIndex = partition(nums, left, right);
        quickSort(nums, left, pivotIndex - 1); // 从左到划分元素前一个做同样操作
        quickSort(nums, pivotIndex + 1, right); // 同理
    }

    public int partition(int[] nums, int left, int right) {
        int randomIndex = left + random.nextInt(right - left + 1);
        /*
            为什么这里要把randomIndex和left进行交换?
            保证数组无序性,因为在数组有序的时候出现递归树倾斜问题
            此时时间复杂度退化到O(n^2)
        */
        swap(nums, left, randomIndex);
        int pivot = nums[left];
        int j = left; // 循环不变量,代表的永远是第一个区间的最后一个元素
        for(int i = left + 1; i <= right; ++i) {
       		// 先++j,在进行交换
            if(nums[i] <= pivot) {
                ++j;
                swap(nums, i, j);
            }
        }
        swap(nums, left, j); // 最后别忘了划分元素和j的交换
        return j;
    }

    public void swap(int[] nums, int i, int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

根据循环不变量的不同的定义,可以写出不同的代码,比如看你怎么定义j的位置(有的人定义j为第二个区间的第一个元素!)
LeetCode的TopK问题
当使用第一版代码的时候会出现一些问题,当数组中出现大量重复元素的时候,此时只通过取随机数是无效的,要把重复元素均分的分到两个区间,这里只写了双路快排(只会双路快排)
在这里插入图片描述

第二版代码

  • 双指针:把等于切分元素的所有元素等概率地分到了数组的两侧,避免了递归树倾斜,递归树相对平衡;
public int partition(int[] nums, int left, int right) {
        int randomIndex = left + r.nextInt(right - left + 1);
        swap(nums, randomIndex, left);
        int pivot = nums[left];
        int le = left + 1; //循环不变量1,指向第一个区间的最后一个右边
        int ge = right;//指向第二个区间的第一个左边
        while(true) {
            while(le <= ge && nums[le] < pivot) {
                le++;
            }
			
            while(le <= ge && nums[ge] > pivot) {
                ge--;
            }
            // 退出循环条件
            // 最终ge要不然大于le一个,要不然等于le
            if(le >= ge) {
                break;
            }
            // 交换le和ge的值,因为le此时指向大于等于pivot,ge指向小于等于pivot
            swap(nums, le, ge);
            le++;
            ge--;
        }
        /*
			这里最终left是一定和ge交换,ge和le相等时,交换谁都可以,但是不相等时
			ge指向第一个区间最后一个元素
		*/
        swap(nums, left, ge);
        return ge;
    }

总结:收获颇多,写文章是做笔记
推荐看liweiwei1419,讲的更好

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值