必刷算法100题之排序数组

题目链接

912. 排序数组 - 力扣(LeetCode)

题目解析

将数组进行升序排序

算法原理

快速排序

先选择基准元素, 然后把数组分成俩部分, 基准左边都是<=key,右边就是>=key.

然后左右区间再重复上述操作

partation

但是里面有元素重复的话, 时间复杂度会退化, 因此我们分成三块

1. 我们用数组分三块的思想, 实现快排, 中间的元素(=key)的不需要处理, <key和>key的需要处理

定义i,left,right,i用来遍历

因此可以分为四块[0,left] 小于key,[left+1,i-1]等于k, [i,right-1] 未扫描 ,[right,n-1] 大于key

分类讨论

nums[i] < key -> 把当前元素加到左边的区域 swap(nums[++left],nums[i++])

nums[i] = key ->i++

nums[i]>key -> 把当前元素加到右边的区域 swap(nums[--right,nums[i])

然后进行递归

数组分三块这个操作是怎么让效率更高?

数组都是重复元素的话, 数组只会执行一次, 因为等于Key

优化:

选择基准元素的方法=> 用随机的方式选择基准元素, left+一个偏移量就是在这个数组上随机取值

  

代码编写

int key = nums[new Random().nextInt(r - l + 1) + l]; 这个里面就是取基准值为[l,r]区间的随机数

class Solution {
    //交换
    private static void swap(int[] nums, int index1, int index2) {
        int tmp = nums[index1];
        nums[index1] = nums[index2];
        nums[index2] = tmp;
    }

    //递归,分三段
    private static void quickSort(int[] nums, int l, int r) {
        if (l > r) {//处理边界情况, l>r=>这个就是递归的出口
            return;
        }
        //l,r是用来固定左右端点的
        int left = l - 1;
        int right = r + 1;
        int key = nums[new Random().nextInt(r - l + 1) + l];//这个是保证区间是在[l,r]区间里面随机取一个中间值
        int i = l;//从区间的开始进行遍历
        while (i < right) {
            //如果是>key
            if (nums[i] < key) {
                swap(nums, ++left, i++);
            } else if (nums[i] == key) {
                //如果是=key
                i++;
            } else {
                //如果是<key
                swap(nums, --right, i);
            }
        }
        //处理左区间
        quickSort(nums, l, left);
        //处理右区间
        quickSort(nums, right, r);
    }

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