欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

jdk1.8 DualPivotQuicksort类简析

程序员文章站 2022-07-14 12:18:10
...

DualPivotQuicksort类主要实现了jdk的排序算法

int型数组

length<47时用插入排序(涉及到pair insertion),47<=length<286,用快排,length>=286时用归并排序

byte、short、char型数据用计数排序。

 

此处主要分析针对int数组的sort方法(该方法实现了插入排序和快排部分),声明如下:

/**

* Sorts the specified range of the array by Dual-Pivot Quicksort.

*

* @param a the array to be sorted

* @param left the index of the first element, inclusive, to be sorted

* @param right the index of the last element, inclusive, to be sorted

* @param leftmost indicates if this part is the leftmost in the range

*/

private static void sort(int[] a, int left, int right, boolean leftmost) {}

此sort中插入排序略过,主要分析快排的实现。

以数组的median值为参考,在其附近以length/7(用length>>3+length>>6+1逼近)为间隔依次取e1、e2、e3(median)、e4、e5并对a[e1]、a[e2] 、a[e3] 、a[e4] 、a[e5]插入排序。

 

1)若以e1、e2、e3、e4、e5为index的值互不相等,则使用双轴快排:

选两个枢纽元pivot1(a[e2])和pivot2(a[e4]),将数组分成如下3部分:

           /*
             * Partitioning:
             *
             *   left part                  center part                        right part
             * +-----------------------------------------------------------------------+
             * |  < pivot1  |  pivot1 <= && <= pivot2  |    ?    |  > pivot2  |
             * +-----------------------------------------------------------------------+
             *                    ^                                        ^     ^
             *                    |                                         |      |
             *                  less                                     k   great
             *
             * Invariants:
             *
             *              all in (left, less)   < pivot1
             *    pivot1 <= all in [less, k)     <= pivot2
             *              all in (great, right) > pivot2
             *
             * Pointer k is the first index of ?-part.
             */

分割完成后对left part和right part再调用此sort方法。

            long pivot1 = a[e2];
            long pivot2 = a[e4];
            /*
             * The first and the last elements to be sorted are moved to the
             * locations formerly occupied by the pivots. When partitioning
             * is complete, the pivots are swapped back into their final
             * positions, and excluded from subsequent sorting.
             */
            a[e2] = a[left];
            a[e4] = a[right];
            /*
             * Skip elements, which are less or greater than pivot values.
             */
            while (a[++less] < pivot1);
            while (a[--great] > pivot2);
            outer:
            for (int k = less - 1; ++k <= great; ) {
                long ak = a[k];
                if (ak < pivot1) { // Move a[k] to left part
                    a[k] = a[less];
                    /*
                     * Here and below we use "a[i] = b; i++;" instead
                     * of "a[i++] = b;" due to performance issue.
                     */
                    a[less] = ak;
                    ++less;
                } else if (ak > pivot2) { // Move a[k] to right part
                    while (a[great] > pivot2) {
                        if (great-- == k) {
                            break outer;
                        }
                    }
                    if (a[great] < pivot1) { // a[great] <= pivot2
                        a[k] = a[less];
                        a[less] = a[great];
                        ++less;
                    } else { // pivot1 <= a[great] <= pivot2
                        a[k] = a[great];
                    }
                    /*
                     * Here and below we use "a[i] = b; i--;" instead
                     * of "a[i--] = b;" due to performance issue.
                     */
                    a[great] = ak;
                    --great;
                }
            }

            // Swap pivots into their final positions
            a[left]  = a[less  - 1]; a[less  - 1] = pivot1;
            a[right] = a[great + 1]; a[great + 1] = pivot2;

            // Sort left and right parts recursively, excluding known pivots
            sort(a, left, less - 2, leftmost);
            sort(a, great + 2, right, false);

center part如果占据整个数组的4/7(less < e1 && e5 < great)以上,则将和两个枢纽元相等的数据分别移动到两边:

               /*
                 * Partitioning:
                 *
                 *   left part                  center part                  right part
                 * +--------------------------------------------------------------------+
                 * | == pivot1 |  pivot1 < && < pivot2  |    ?    | == pivot2 |
                 * +--------------------------------------------------------------------+
                 *                     ^                                  ^      ^
                 *                     |                                   |       |
                 *                   less                               k    great
                 *
                 * Invariants:
                 *
                 *              all in (*,  less) == pivot1
                 *     pivot1 < all in [less,  k)  < pivot2
                 *              all in (great, *) == pivot2
                 *
                 * Pointer k is the first index of ?-part.
                 */

然后再对center part调用sort方法

                /*
                 * Skip elements, which are equal to pivot values.
                 */
                while (a[less] == pivot1) {
                    ++less;
                }

                while (a[great] == pivot2) {
                    --great;
                }
                outer:
                for (int k = less - 1; ++k <= great; ) {
                    long ak = a[k];
                    if (ak == pivot1) { // Move a[k] to left part
                        a[k] = a[less];
                        a[less] = ak;
                        ++less;
                    } else if (ak == pivot2) { // Move a[k] to right part
                        while (a[great] == pivot2) {
                            if (great-- == k) {
                                break outer;
                            }
                        }
                        if (a[great] == pivot1) { // a[great] < pivot2
                            a[k] = a[less];
                            /*
                             * Even though a[great] equals to pivot1, the
                             * assignment a[less] = pivot1 may be incorrect,
                             * if a[great] and pivot1 are floating-point zeros
                             * of different signs. Therefore in float and
                             * double sorting methods we have to use more
                             * accurate assignment a[less] = a[great].
                             */
                            a[less] = pivot1;
                            ++less;
                        } else { // pivot1 < a[great] < pivot2
                            a[k] = a[great];
                        }
                        a[great] = ak;
                        --great;
                    }
                }
            }

            // Sort center part recursively
            sort(a, less, great, false);

 

2) 若以e1、e2、e3、e4、e5为index的值有两个以上相等,则使用单个枢纽元(pivot取a[e3])的三路方案:

            /*
             * Partitioning degenerates to the traditional 3-way
             * (or "Dutch National Flag") schema:
             *
             *   left part    center part              right part
             * +--------------------------------------------------+
             * |  < pivot  |   == pivot   |     ?    |  > pivot  |
             * +--------------------------------------------------+
             *                  ^                  ^      ^
             *                  |                   |       |
             *                less               k    great
             *
             * Invariants:
             *
             *   all in (left, less)   < pivot
             *   all in [less, k)     == pivot
             *   all in (great, right) > pivot
             *
             * Pointer k is the first index of ?-part.
             */

分割后对left part和right part再调用此sort方法。

            long pivot = a[e3];
            for (int k = less; k <= great; ++k) {
                if (a[k] == pivot) {
                    continue;
                }
                long ak = a[k];
                if (ak < pivot) { // Move a[k] to left part
                    a[k] = a[less];
                    a[less] = ak;
                    ++less;
                } else { // a[k] > pivot - Move a[k] to right part
                    while (a[great] > pivot) {
                        --great;
                    }
                    if (a[great] < pivot) { // a[great] <= pivot
                        a[k] = a[less];
                        a[less] = a[great];
                        ++less;
                    } else { // a[great] == pivot
                        /*
                         * Even though a[great] equals to pivot, the
                         * assignment a[k] = pivot may be incorrect,
                         * if a[great] and pivot are floating-point
                         * zeros of different signs. Therefore in float
                         * and double sorting methods we have to use
                         * more accurate assignment a[k] = a[great].
                         */
                        a[k] = pivot;
                    }
                    a[great] = ak;
                    --great;
                }
            }

            /*
             * Sort left and right parts recursively.
             * All elements from center part are equal
             * and, therefore, already sorted.
             */
            sort(a, left, less - 1, leftmost);
            sort(a, great + 1, right, false);

 

相关标签: 源码分析