博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
数据结构学习笔记3.2—快速排序
阅读量:4323 次
发布时间:2019-06-06

本文共 6779 字,大约阅读时间需要 22 分钟。

快速排序是目前最流行的算法,在大多数情况下,快速排序都是最快的,执行时间为O(N*logN)。

快速排序本质上是通过把一个数组划分为两个子数组,然后在递归调用自身对每一个字数组进行快速排序。

即数组划分为字数组A,B,在对A进行划分为A1,A2,对A1划分为A11,A12。。B也是如此执行,这就涉及到递归调用自身的操作。

1.取右值作为枢纽的情形

/**     * 快速排序     *      * @param left     * @param right     */    public static void quickSort(int left, int right, int[] arr)    {        // 检查数据是否只包含一个数据项,如果是,则为有序数组,无需排序        if (right - left <= 0)        {            return;        }        else        {            // 设置参照值为最右边的数据值            int pivot = arr[right];            int partition = partitionIt(left, right, pivot, arr);            // 调用自身对左边排序            quickSort(left, partition - 1, arr);            // 调用自身对右边排序            quickSort(partition + 1, right, arr);        }    }

(1)设置枢纽:int pivot = arr[right]; 为了方便,这里选择做右边的作为枢纽。当一轮排序下来后,枢纽的位置在最终排序的位置就确定了,

这是因为,最终排序后,左边的数值总比枢纽的小,右边的数值总比枢纽的大。

 

(2)这里看到主要有3个方法

      1)partitionIt(left, right, pivot, arr); 数据结构学习笔记3.1节所描述的划分方法,主要是取出枢纽值。

      2)quickSort(left, partition - 1, arr); 递归调用自身排序左边的数组。

      3)quickSort(partition + 1, right, arr); 递归调用自身排序右边的数组。

递归方法可以这样理解:先把左边排好,再排列右边。

 

完整代码:

/**     * 快速排序     *      * @param left     * @param right     */    public static void quickSort(int left, int right, int[] arr)    {        // 检查数据是否只包含一个数据项,如果是,则为有序数组,无需排序        if (right - left <= 0)        {            return;        }        else        {            // 设置参照值为最右边的数据值            int pivot = arr[right];            int partition = partitionIt(left, right, pivot, arr);            // 调用自身对左边排序            quickSort(left, partition - 1, arr);            // 调用自身对右边排序            quickSort(partition + 1, right, arr);        }    }    /**     * 划分数据     *      * @param left 左边数据     * @param right 右边数据     * @param pivot 参照值     * @return     */    public static int partitionIt(int left, int right, int pivot, int[] arr)    {        // 左边指针        // 左边数据-1,为了后面执行++leftPrt操作时,数据能正确+1        int leftPrt = left - 1;        // 右边指针        // 右边数据-1,为了后面执行--rightPrt操作时,数据能正确-1        int rightPrt = right + 1;        while (true)        {            // 左边数据比参照值的小,不做后续操作,循环            while (leftPrt < arr.length && arr[++leftPrt] < pivot)            {                int a = 0;            }            // 右边数据比参照值的大,不做后续操作,循环            while (rightPrt > 0 && arr[--rightPrt] >= pivot)            {                int b = 0;            }            if (leftPrt >= rightPrt)            {                break;            }            else            {                // 交左右两边的数据                swap(leftPrt, rightPrt, arr);            }        }        // 重置参照值        swap(leftPrt, right, arr);        return leftPrt;    }    /**     * 数据交换     *      * @param index1 入参1     * @param index2 入参2     * @param arr 比较数组     */    public static void swap(int index1, int index2, int[] arr)    {        int temp = arr[index1];        arr[index1] = arr[index2];        arr[index2] = temp;    }

 

2.取中值作为枢纽的情形

对于快速排序算法来说,最优的情况是有两个大小相等的子数组。

以右值作为枢纽,有一种情形:当数组以逆序排序列时,这时候产生N-1的子数组和一个只包含枢纽的子数组。此时执行的效率为O(N2)。

 

解决方法:

三数据项取中:思路是取数组N的第1个,第N/2个,第N个数据,即arr[0],arr[N/2],,arr[N-1]项数据,此时操作是:

(1)比较三项数据,取中间值作为枢纽。

(2)对这三个数据排序。

 

完整代码:

/**     * 快速排序     * @param left 数组第一个位置,即0     * @param right 数组最后一个数据,即arr.length - 1     * @param arr 数组     */    public static void recQuickSort(int left, int right, int[] arr)    {        // 数组长度        int size = right - left + 1;                // 如果数据项只有3项        if (size <= 3)        {            // 处理数组个数小于3个的数组            manualSort(left, right, arr);        }        else        {            int median = midanOf3(left, right, arr);            int partition = partitionIt(left, right, median, arr);            recQuickSort(left, partition - 1, arr);            recQuickSort(partition, right, arr);        }    }        /**     * 处理整个数组小于或等于3个数据项的数组     * @param left 数组左边位置     * @param right 数组右边位置     * @param arr 数组     */    public static void manualSort(int left, int right, int[] arr)    {        int size = right - left + 1;        if (size <= 1)        {            return;        }        if (size == 2)        {            // 如果左边数据大于右边数据,则交换位置            if (arr[left] > arr[right])            {                swap(left, right, arr);            }            return;        }        // 数组长度为3        else        {            // 交换位置,交换方法:先用第一个数据项一次于后面两个数据项比较,            // 取出最小的数据放在最左边,然后在比较后面两个数据            if (arr[left] > arr[right - 1])            {                swap(left, right, arr);            }            if (arr[left] > arr[right])            {                swap(left, right, arr);            }            if(arr[right- 1] > arr[right])                {                swap(right - 1, right, arr);            }        }    }        /**     * 取出3个数据项,排序,并将中枢取出放在数组right - 1位置     * @param left 数组左边项     * @param right数组右边项     * @param arr 数组     */    public static int midanOf3(int left, int right, int[] arr)    {        // 取出中间项        int center = (right + left) / 2;                // 通过交换排序,思路与manualSort中使用的方法一致        if (arr[left] > arr[center])        {            swap(left, center, arr);        }        if (arr[left] > arr[right])        {            swap(left, right, arr);        }        if (arr[center] > arr[right])        {            swap(center, right, arr);        }                // 交换中枢与right - 1的位置        // 由于arr[right]的位置已经排好,即数值比中枢的要打,已经划分        // 所以取倒数第二个数据right - 1的数据,将center中枢的位置与之交换,目的减少右边排序次数        swap(center, right - 1, arr);                // 此时位置值为中枢值        return arr[right - 1];     }        /**     * 划分数组     * @param left 数组左边位置     * @param right 数组右边位置     * @param median 中枢值     * @param arr 数组     */    public static int partitionIt(int left, int right, int median ,int[] arr)    {        int leftPrt = left;        int rightPrt = right - 1;                 while(true)        {            while (arr[++leftPrt] < median);            while (arr[--rightPrt] > median);                        if (leftPrt >= rightPrt)            {                break;            }            else            {                // 左边大于中枢值,右边小于中枢值,交换                swap(leftPrt, rightPrt, arr);            }        }                // 重置中枢值,此时左边leftPrt指针的位置指向右边数组的第一个数据项,比中枢值大        // 将左边标记的位置与right - 1交换,此时,中枢值所在的位置已经固定,不会在变        swap(leftPrt, right - 1, arr);        return leftPrt;    }        /**     * 数据交换     *      * @param index1 入参1     * @param index2 入参2     * @param arr 比较数组     */    public static void swap(int index1, int index2, int[] arr)    {        int temp = arr[index1];        arr[index1] = arr[index2];        arr[index2] = temp;    }

1. manualSort(left, right, arr);这个方法是处理当数组划分后,数组个数小于等于3个时,此时直接交换排序排序即可。

2.midanOf3(left, right, arr);这个方法是去数组(包含子数组)的中间值,也是通过比较交换的方法排序。

 

取中值排序的特点:对于随机排序,执行效果与取右值相差不大,但是在逆序中,排序消耗时间有明显的提高。

转载于:https://www.cnblogs.com/winlrou/p/3529089.html

你可能感兴趣的文章
Git报错:insufficient permission for adding an object to repository database .git/objects
查看>>
ajax跨域,携带cookie
查看>>
BZOJ 1600: [Usaco2008 Oct]建造栅栏( dp )
查看>>
洛谷 CF937A Olympiad
查看>>
Codeforces Round #445 C. Petya and Catacombs【思维/题意】
查看>>
用MATLAB同时作多幅图
查看>>
python中map的排序以及取出map中取最大最小值
查看>>
ROR 第一章 从零到部署--第一个程序
查看>>
<form>标签
查看>>
vue去掉地址栏# 方法
查看>>
Lambda03 方法引用、类型判断、变量引用
查看>>
was集群下基于接口分布式架构和开发经验谈
查看>>
MySQL学习——MySQL数据库概述与基础
查看>>
ES索引模板
查看>>
HDU2112 HDU Today 最短路+字符串哈希
查看>>
JPanel重绘
查看>>
图片放大器——wpf
查看>>
SCALA STEP BY STEP
查看>>
cocos2d-x学习笔记
查看>>
MySql中的变量定义
查看>>