快速排序算法重新梳理

1、梳理背景

最近在查阅关于快速排序算法的相关实现,发现关于算法原理、源码实现出现不同的变种版本,个人觉得有些版本不好理解,有些版本更易于理解。参考了各种不同的版本,通过自己实践,最终将此算法的原理、及源码实现进行了全新的梳理,更易于掌握。

2、算法原理

给定一个数字类的数组(arr[]),选定一个基准数,一般以数组第1个数作为基准数,在数组的最左侧(位置为0,设置为left)及数组的最右侧(java,如数组长度为6,位置为5,设置为right)。

第一步:先从右往左查找

开始执行查找,必须【先从右侧往左】进行查找(即 right- -),直到找到第1个小于基准数的数,然后记下right的位置,则从右往左查找结束;如果直到 right- -=0 都还没找到小于基准数的数,则从右往左查找结束。

第二步:在从左往右查找

继续接着上一步操作,先判断当前left(当前是0)是否小于当前right的值。

如果,left < right 说明左右位置还未相遇,则开始继续【从左往右】进行查找(即left+ +),直到找到第1个大于基准数的数,然后记下left的位置,则从左往右查找结束;否则(即left>=right),说明左右已经相遇,则从左往右查找结束。

第三步:交换值

继续接着上一步操作,如果,左和右都找到了比基准数大和小的数,且当前left的位置小于当前right的位置(left < right),则交换arr[left]和arr[rght]的值;

第四步:左右相遇

当左右相遇时,结束查找,并将相遇的数与基准数交换,返回交换后基准值的位置(k)。到此,第一轮排序完成。

第五步:继续递归调用重复上面的123步骤

当第一轮执行完成后,会返回上一次基准值的位置(k),此时我们发现,位于k左边的数都是小于基准值的数(左子数组),位于k右边的数都是大于基准值的数(右子数组)。但是,左子数组、右子数组依然是乱序的,所以,我们可以对左右子数组进行分别排序。这时候我们就可以重复前面的12345步骤(递归调用)对子数组进行排序,直到左右子数组全部排序完成。

关键核心点:
  • 核心思想:就是将数组以基准点开始,找到分割点k,将数组一分为二,再针对子数组,继续选定子基准点开始,查找子数组的分割点k,再将子数组一分为二,如此重复,直到全部排序完成。
  • 重合点查找情况1,一定是左边递增序列值>=右边递减序列值代表左右相遇了,本次右左查找完成,程序继续往下,还需执行一次动作,即将基准值与相遇位置的值进行交换,目的是为了将大于基准数和小于基准数的数,分隔到基准数的两边。
  • 重合点查找情况2,我们选定位置0的值为基准数,如果从右往左,发现right- - = 0,代表都没找到一个小于基准值的数,这时候停下来,开始从左往右查找,left从0开始递增时,发现left=right=0,这时候左右序列值在0处相遇了,由于没有找到合适的值,所以直接将基准数(即位置0的值)和位置0对应的数交换,自己与自己交换一次,本轮查找执行完成。
  • 查找顺序,一定是先从右往左,后从左往右。

3、时间复杂度

最优情况下时间复杂度:O( nlogn );
最差情况下时间复杂度:O( n^2 );

4、空间复杂度

最优的情况下空间复杂度为:O(logn) ;每一次都平分数组的情况
最差的情况下空间复杂度为:O( n ) ;退化为冒泡排序的情况

5、源码实现

    public static void main(String[] args) {
        int[] arr = {6, 1, 2, 7, 9, 3, 4, 5, 10, 8};
        showResult(arr);
        mqs(arr, 0, arr.length - 1);
        showResult(arr);
    }
    
   private static void mqs(int[] arr, int low, int high) {
        int pivot;
        if (low < high) {
            //获取上一次基准数的位置
            pivot = myQuickSort(arr, low, high);
            //从上一次基准数的左右两边继续交换
            mqs(arr, low, pivot - 1);
            mqs(arr, pivot + 1, high);
        }
    }
	private static int myQuickSort(int[] arr, int startIndex, int endIndex) {
        //查找基准值 选取第一个数
        int pVal = arr[startIndex];
        int left = startIndex;
        int right = endIndex;

        //当i与j相遇时循环暂停
        while (left != right) {

            //前置条件,左边必须小于右边,代表两端已经相遇,从右往左扫描完成
            //先从右往左扫描,如果找到第一个小于基准的数,直接跳出循环
            while (left < right && arr[right] > pVal) {
                right--;
            }
            //前置条件,左边必须小于右边,否则就代表两端已经相遇,从左往右扫描完成
            //接着从左往右扫描,如果找到第一个大于基准的数,直接跳出循环
            while (left < right && arr[left] <= pVal) {
                left++;
            }
            //从右边和左边都找到了对应的,小于基准值的数和大于基准值的数,且还左边和右边的位置还未相遇,则直接交换值
            if (left < right) {
                changePos(arr, left, right);
                System.out.println("========特殊开始=========");
                showResult(arr);
                System.out.println("========特殊结束=========");
            }
        }
        //第一轮完成,让startIndex和right重合的位置的值和基准值交换,返回基准的位置
        changePos(arr, startIndex, right);
        showResult(arr);
        return left;
    }
	
	private static void showResult(int[] arr) {
        System.out.println("------------------");
        for (int i = 0; i < arra.length; i++) {
            System.out.println(arr[i]);
        }
    }

    private static void changePos(int[] arr, int i, int j) {
        int temp;
        temp = arr[i];
        arr[i] = arr[j];
        arr[j] = temp;
    }

输出结果:

...
------------------
1
2
3
4
5
6
7
8
9
10

6、图片演示

在这里插入图片描述
在这里插入图片描述

参考链接:

排序算法之 快速排序 及其时间复杂度和空间复杂度.

算法 3:最常用的排序——快速排序.

神级基础排序——快速排序.






原创不易,求个关注。

在这里插入图片描述

微信公众号:一粒尘埃的漫旅
里面有很多想对大家说的话,就像和朋友聊聊天。
写代码,做设计,聊生活,聊工作,聊职场。
我见到的世界是什么样子的?
搜索关注我吧。

公众号与博客的内容不同。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值