LeetCode每日一题(题215,快速排序)

模板

快速排序作为一个经典的排序算法有着高效稳定的优势,快速排序的思路也非常简单。首先我们选取一个数作为基数,然后把所有比这个数小的数字放在左边,大的放在右边,然后再以这个数字为分割线,对左右两边的数组继续排序。 由此我们可以写出快速排序的经典模式(升序):

 public void quickSort(int[] nums,int start,int end){
     if(start >= end)
         return;
     int mid = partition(nums,start,end);
     quickSort(nums,start,mid-1);
     quickSort(nums,mid+1,end);
 }
 public int partition(int[] nums,int start,int end){
     if(start >= end)
         return start;
     int tmp = nums[start];
     int i = start;
     int j = end;
     while(i<j){
         //注意这边要使用>=,
         //使用>会导致两个数反复交换然后进入死循环
         while(i<j && nums[j]>=tmp) j--;
             nums[i] = nums[j];
         while(i<j && nums[i]<=tmp) i++;
             nums[j] = nums[i];
     }
     //此时i和j相等
     nums[i] = tmp;
     return i;
 }

数组中第K个最大元素

题目:https://leetcode-cn.com/problems/kth-largest-element-in-an-array/
题目大意:在未排序的数组中找到第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。 分析:这道题的一种比较快速的解法就是进行快速排序,一旦快速排序后返回的位置是k-1,那说明左边k个数已经是最大的了,直接返回。
代码:

class Solution {
    public int findKthLargest(int[] nums, int k) {
        if(nums == null || nums.length == 0)
            return 0;
        if(k>nums.length)
            k = nums.length;
        quickSort(nums,0,nums.length-1, k);
        return nums[k-1];
    }
    public void quickSort(int[] nums,int start,int end,int k){
        if(start >= end)
            return;
        int mid = partition(nums,start,end);
        if(mid == k-1) 
            return;
        else if(mid > k-1)
            quickSort(nums,start,mid-1, k);
        else
            quickSort(nums,mid+1,end, k);
    }
    public int partition(int[] nums,int start,int end){
        if(start >= end)
            return start;
        //设定基准数
        int tmp = nums[start];
        int i = start;
        int j = end;
        while(i<j){
            //注意这边要使用>=,
            //使用>会导致两个数反复交换然后进入死循环
            while(i<j && nums[j]<=tmp) j--;
                nums[i] = nums[j];
            while(i<j && nums[i]>=tmp) i++;
                nums[j] = nums[i];
        }
        //此时i和j相等
        nums[i] = tmp;
        return i;
    }
}

优化

按照上面的代码确实可以AC,但是我们可以看到时间效率和空间效率其实并不高。
在这里插入图片描述
我们先来分析一下为什么这种快速排序效率不高。
快速排序的思想是把一个数组分为两等分,如果我们每次找到的基准数排序之后刚好在正中间,那么经过 log n \log_{}n 次排序就能将整个数组排好序了。
但是我们是采用的数组第一个元素作为基准数,那么当数组是升序或者降序排列的时候,我们每次划分数组都是划分为了1和n-1两个极端情况,导致快速排序的效率大大降低,达到了   o ( n 2 ) \ o(n^{2})
所以问题就成了如何更好的找到基准数。
我们可以采用随机数,我们在partition函数选取基准数的位置插入一段代码:

Random random = new Random(System.currentTimeMillis());
int randomIndex = random.nextInt(r-l+1)+l;
int tmp = nums[randomIndex];
nums[randomIndex] = nums[start];
nums[start] = tmp;

在这里插入图片描述
可以看到效率大大提升了。
当然其实还有更加优秀的优化方法,每次选取最左边、最右边、最中间三个数中的中间值,以这个中间值作为基准数进行划分。

猜你喜欢

转载自blog.csdn.net/qq_33241802/article/details/107014461