快速排序以及其优化

快速排序及其优化

写在前面

最开始学习数据结构中的快排时,使用的是首尾指针方法,但是我自己觉得这个实现起来不方便,因为去学习了别人的方法。
这写方法是看了哔哩哔哩一个up的视频,看完之后感叹太妙了,遂写个文章分享一下。

快慢指针快排

定义fast = 0, slow = 0,给定数组nums,初始状态下,定义left = 0, right = nums.length - 1,遍历数组,每次比较fastright位置的元素,如果nums[fast] < nums[right,则交换fastslow位置的元素,然后slow++,fast++,否则只移动快指针fast++,当fast == right时停止遍历。遍历完成后再次交换slowfast的位置。
本质上,也是一趟排序一个数,也就是nums[right],最后一次交换就是将right位置的值换到合适的位置,使得左边一定比它小,右边一定比他大。

代码
class Solution {
    public static void quickSort(int[] nums, int left, int right) {
        if (left >= right) return;
        int t = nums[right];
        int slow = left, fast = left;
        while (fast < right) {
            if (nums[fast] < t) {
                swap(nums, fast, slow);
                slow++;
            }
            fast++;
        }
        swap(nums, slow, fast);
        quickSort(nums, left, slow - 1);
        quickSort(nums, slow + 1,right);
    }

    public static void swap(int[] arr, int index1, int index2) {
        int temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }

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

但是,由于该方法每次选择的中间值都是nums[right],如果数组已经排好序的情况下,则会做很多无用功。在力扣912数组排序中,这个写法会超时。

优化1

使用随机下标替代right,随机生成一个下标作为中间值的下标,再将该下标的值与right下标的值互换。

代码
class Solution {
    public static void quickSort(int[] nums, int left, int right) {
        if (left >= right) return;
        // 生成随机值
        Random random = new Random();
        int r = random.nextInt(right - left + 1) + left;
        swap(nums, r, right);
        int t = nums[right];
        int slow = left, fast = left;
        while (fast < right) {
            if (nums[fast] < t) {
                swap(nums, fast, slow);
                slow++;
            }
            fast++;
        }
        swap(nums, slow, fast);
        quickSort(nums, left, slow - 1);
        quickSort(nums, slow + 1,right);
    }

    public static void swap(int[] arr, int index1, int index2) {
        int temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }

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

运行时间为:1840ms
这种方法提升了一定的效率,但是,如果遇到数组中有大量相同的值,例如[1,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,1…],则会也会对2进行排序,因此,对于相同的值,我们因该不需要再进行排序。

优化2

为了不对相同的值进行排序,重新定义指针less = left,i = left,great = right。假设数组中有大量相同的值x我们需要达到的效果为:less的左边都是小于x的值,great右边都是大于x的值,lessgreat之间都是x

代码
class Solution {
    public static void quickSort(int[] nums, int left, int right) {
        if (left >= right) return;
        Random random = new Random();
        int r = random.nextInt(right - left + 1) + left;
        swap(nums, r, right);
        int t = nums[right];
        int i = left, less = left;
        int great = right;
        while (i <= great) {
            if (nums[i] < t) {
                swap(nums, i, less);
                i++;
                less++;
            } else if (nums[i] > t) {
                swap(nums, i, great);
                great--;
            }else {
                i++;
            }
        }
        quickSort(nums, left, less - 1);
        quickSort(nums, great + 1,right);
    }

    public static void swap(int[] arr, int index1, int index2) {
        int temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }

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

运行时间为:39ms

优化3

到这里,快排的效率已经被优化的很不错了,但是还有优化空间,查看JAVA中Arrays.sort()源码可以知道,当数组元素小于44个时,会直接使用插入排序,如下图:
在这里插入图片描述
在这里插入图片描述
因此我们也可以写一个插入排序,按照这个逻辑来执行。

代码
class Solution {

    public static void insertSort(int[] nums, int left, int right) {
        int len = right - left + 1;
        int index = left + 1;
        while (index <= right) {
            for (int i = index;i > left; i--) {
                if (nums[i] < nums[i - 1]) {
                    swap(nums, i, i - 1);
                }else {
                    break;
                }
            }
            index++;
        }
    }

    public static void quickSort(int[] nums, int left, int right) {
        if (left >= right) return;
        if (right - left + 1 < 44) {
            insertSort(nums, left, right);
            return;
        }
        Random random = new Random();
        int r = random.nextInt(right - left + 1) + left;
        swap(nums, r, right);
        int t = nums[right];
        int i = left, less = left;
        int great = right;
        while (i <= great) {
            if (nums[i] < t) {
                swap(nums, i, less);
                i++;
                less++;
            } else if (nums[i] > t) {
                swap(nums, i, great);
                great--;
            }else {
                i++;
            }
        }
        quickSort(nums, left, less - 1);
        quickSort(nums, great + 1,right);
    }

    public static void swap(int[] arr, int index1, int index2) {
        int temp = arr[index1];
        arr[index1] = arr[index2];
        arr[index2] = temp;
    }

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

运行时间为:22ms

以下是我的leetcode运行截图:
在这里插入图片描述

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值