快速排序算法的两种Java实现及图解

快速排序算法的两种Java实现及图解

        十大经典排序算法中,感觉“快速排序算法”不太容易理解,这里简单做个笔记,利用图示直观的展示了其排序原理。

        快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

算法描述

快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:

  • 从数列中挑出一个元素,称为 “基准”(pivot);
  • 重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
  • 递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。

        快速排序实现的重点在于数组的拆分,通常我们将数组的第一个元素定义为比较元素,然后将数组中小于比较元素的数放到左边,将大于比较元素的放到右边,

        这样我们就将数组拆分成了左右两部分:小于比较元素的数组;大于比较元素的数组。我们再对这两个数组进行同样的拆分,直到拆分到不能再拆分,数组就自然而然地以升序排列了。(如图可简明直观地展现quickSort的思想)。

 

         拆分方法是整个快速排序中的核心,快速排序拥有非常多的拆分方式,这里介绍其中的两种:单指针遍历法与双指针遍历法(在下文中用英文单词split和partition称呼)[参考博客园 算法设计:两种快速排序代码实现]

1. split法

用单个指针。

       首先将数组首元素设置为“基准值pivot”(即其他所有值都需要跟其比较大小),然后将第二个开始的元素依次与基准值比较,如果大于基准值pivot则跳过,如果小于pivot,则将其与前面较大的元素进行交换,将数组中所有元素交换完毕后,再将基准值pivot放到中间位置。

图解:

Java代码实现:

    /**
     * (6)-6.1 快速排序`方法
     * 单指针方法-split
     * @param arr
     * @param low    // start
     * @param high   // end
     * @return
     */
    public static void quickSort(int arr[], int low, int high)
    {
        if(low < high)
        {
            int i = split(arr, low, high);    //划分并获取比较元素的位置
            quickSort(arr, low, i-1);     //对比较元素左边的数组进行排序
            quickSort(arr, i+1, high);     //对比较元素右边的数字进行排序
        }
    }
    //划分数组
    public static int split(int arr[], int low, int high)
    {
        int i = low;    //i指向比较元素的期望位置
        int x = arr[low];    //将该组的第一个元素作为比较元素
        //从第二个元素开始,若当前元素大于比较元素,将其跳过
        for(int j = low+1; j <= high; j++)
            //若找到了小于比较元素的元素,将其与前面较大的元素进行交换
            if(arr[j] <= x)
            {
                i++;
                if(i != j)
                    swap(arr, i, j);

            }
        swap(arr, i, low);     //将比较元素交换到正确的位置上
        return i;    //返回比较元素的位置
    }
    //交换方法
    public static void swap(int arr[], int i, int j)
    {
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

2. partition法

双指针实现。

       使用头尾两个方向相反的指针进行遍历,先将数组第一个元素设置为“基准值”或者叫“比较元素”,头指针从左至右找到第一个大于比较元素的数,尾指针从右至左找到第一个小于比较元素的数,全部交换完毕后将比较元素放到中间位置。

图解:

partition图解完整流程:

Java代码实现

    /**
     * (6)-6.2 快速排序
     * 双指针方法-partition
     * @param arr
     * @param low    // start
     * @param high   // end
     * @return
     */
    public static void quicksort2(int arr[], int low, int high)
    {
        if(low < high)
        {
            int i = partition(arr, low, high);    //划分数组并获取比较元素的位置
            quicksort2(arr, low, i-1);     //对比较元素左边进行排序
            quicksort2(arr, i+1, high);     //对比较元素右边进行排序
        }
    }
    //划分数组
    public static int partition(int arr[], int low, int high)
    {
        int x = arr[low];    //将该数组第一个元素设置为比较元素
        int i=low;
        int j=high;
        while(i < j)
        {
            while(i<j && arr[j] >= x)
                j--;     //从右至左找到第一个小于比较元素的数
            while(i<j && arr[i] <= x)
                i++;     //从左至右找到第一个大于比较元素的数
            /*需要注意的是,这里的j--与i++的顺序不可以调换!
             *如果调换了顺序,i会走过头,以至于将后面较大的元素交换到数组开头*/

            //将大数与小数交换
            if(i!=j)
                swap(arr, i, j);
        }
        swap(arr, i, low);    //将比较元素交换到期望位置
        return i;     //返回比较元素的位置
    }
    //交换函数
    public static void swap(int arr[], int i, int j)
    {
        int t = arr[i];
        arr[i] = arr[j];
        arr[j] = t;
    }

测试用例:

int data1[] = new int[] {3,5,38,15,36,26,27,2,4,19,46,48,47,50,44};
int[] data2 = {8,9,1,7,2,3,5,4,6,0};
int data3[] = {5,7,1,6,4,8,3,2};

测试结果:
[2, 3, 4, 5, 15, 19, 26, 27, 36, 38, 44, 46, 47, 48, 50]

[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
[1, 2, 3, 4, 5, 6, 7, 8]

参考:

主要参考这两篇文章,侵权删。

https://www.cnblogs.com/sunriseblogs/p/10009890.html 

https://www.jianshu.com/p/c1efd6a8bb95 (十大经典排序算法总结)

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值