912. 排序数组(快速排序)

912. 排序数组(快速排序)

给你一个整数数组 nums,请你将该数组升序排列。

示例 1:

输入:nums = [5,2,3,1]
输出:[1,2,3,5]

示例 2:

输入:nums = [5,1,1,2,0,0]
输出:[0,0,1,1,2,5]

这道题是LeetCode的912题,要求将一个数组排序,我们本文给出其快速排序的解法。

这次直接先上代码:

class Solution {
    Random random = new Random();

    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){ 
  //一定要加判断,否则在测试用例1中如果随机到0,那么5将会被移到数组的末尾,返回的index为3 
  //那么下次划分区间的时候,[index+1,right]的区间值就会变成了[4,3] 
  //会报IllegalArgumentException非法参数异常

            int index = partition(nums,left,right);
            quickSort(nums,left,index-1);
            quickSort(nums,index+1,right);
        }
    }

    public int partition(int[] nums,int left,int right){
        int randomIndex = random.nextInt(right-left+1)+left;
        swap(nums,left,randomIndex);

        int lt = left;
        int privot = nums[left];
        
        for(int i=left+1; i<=right; i++){
            if(lt <= right && nums[i] < privot){
                lt++;
                swap(nums,lt,i);
            }
        }
        swap(nums,lt,left);
        return lt;
    }

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

我们可以看到,快拍的中心思想,就是每次将一个数字放到它自己本来的位置,然后这个数字就将整个数组分成了两个数组,再对这两个数组进行递归处理即可。

quickSort为快排函数,调用partition函数,拿到返回的位置索引,我们就可以根据这个索引将原数组分割为两个子数组,做递归处理。

partition为快排函数中的核心函数,该函数的作用是从给定数组的给定区间中随机选取一个数字,然后将这个数字放在其排序后应在的位置,并将这个位置的索引返回。

swap函数是一个工具函数,主要作用是将给定数组中下标为i和下标为j的元素做交换。

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(n)

下面举例说明

第一次调用quickSort,传入参数数组nums,left = 0 , right = nums.length-1=5

image-20211005124223310

假设第一次产生的随机数为random.nextInt(right-left+1)+0 = 3,这个数就是我们选中做枢纽元素的树,接下来我们会找到他在排序好的数组中的位置,并将它放回去。也就是说,最终这个元素左边的数都不大于它,这个元素右边的数都不小于它。

image-20211005124707091

交换nums[left]nums[randomIndex]的位置,即交换nums[0]nuns[3]的位置。

image-20211005124720000

我们用变量privot来保存这个枢纽元素的值,用变量lt来指向这个数组最左边的位置。然后从数组下标i = left+1i =1开始,向后遍历数组,将每个比枢纽元素大的数和x和x后面第一个小于等于枢纽元素的数交换位置。

可以看出,指针lt是用来指向大于枢纽元素privot的数的,刚开始lti一起向后走,当遇到比privot大的数的时候,lt停下来记录这个数的位置,而指针i继续向后走的过程中,一旦发现小于privot的数,便和lt指向的数交换位置,重复此过程。

image-20211005135902746

image-20211005141009272

i=3时,nums[i]=5 > privot=3,此时,lt不再继续随着指针i向前移动,而是留下来定位这个比privot大的元素。

image-20211005141104520

随着i继续向前移动,找到了第一个比privot小的值,将其和刚才比privot大的值进行交换位置。

image-20211005141133391

image-20211005141835440

随后,ilt一起向前,如果i碰到比privot小的元素就会和lt交换位置,保证比privot小的元素全部在lt的前面。

image-20211005141914649

image-20211005141929924

至此我们可以确定当前lt位置的左边都是比privot小的值(left除外,里面保存着privot的值),lt位置的右边都是比privot大的值,然后将nums[left]nums[lt]交换位置

image-20211005142145145

此时,lt的值为privot=2,其位置左边的数值皆小于等于privot,其位置右边的数值皆大于privot,此时元素privot已经处于其最终的位置,返回值lt = 4

我们接下来需要对nums数组的[0,3]区间和[5,5]区间进行排序,由于5=5 所以我们只需要对[0,3]区间执行快速排序。

先来一起分析快速排序核心代码partition的执行过程。

这次我们假设随机值为randomIndex=random.nextInt(right-left+1)+left =1

image-20211005142815905

交换leftrandomIndex的位置

image-20211005142854646

不巧的是,这次给定的区间范围内没有比privot大的值,于是lti均指向了元素nums[right],交换了当前nums[lt]nums[left]元素的位置,返回lt=3

image-20211005143354285

根据递归要求,我们接下来需要对区间[0,2]和区间[4,3]进行快速排序,显然[4,3]区间范围不合法。

于是我们对nums数组的[0,2]区间进行快速排序。

一起分析快速排序核心代码partition的执行过程。

这次我们假设随机值为randomIndex=random.nextInt(right-left+1)+left =1

image-20211005143747526

同样的过程,交换位置,遍历数组。

image-20211005143751752

虽然找到了比privot大的数,但由于在right位置,所以不用交换了,返回lt=1

image-20211005143940813

按照递归,应当与nums数组的区间[0,0]和[2,2]区间进行快速排序,因为其均不满足left<right的条件,故递归结束。

整个快速排序过程结束。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值