Let the interviewer satisfaction of sorting algorithms (Photo parsing)

Let the interviewer satisfaction of sorting algorithms (Photo parsing)

  • This sorting algorithm that allows the interviewer smiling

  • This sorting algorithm sorting algorithm set each epitomize

  • This sorting algorithm logic full

  • This sorting algorithm can demonstrate their understanding of the underlying Java

    This sorting algorithm from Vladimir Yaroslavskiy, Jon Bentley and Josh Bloch three large cattle hand, it is the JDK sorting algorithm --java.util.DualPivotQuicksort (double pivot fast row)

DualPivotQuicksort

Look at a logic diagram (if wrong please correct me in the comments area Daniel)

Inserted row refers to the improved version of the Strip - Sentinel inserted row

Refers to the quick drain improved version of fast row - double pivot fast row

Object DualPivotQuickSort not sorted array logic, this logic Arrays are, like the sort merge + Tim

The image should be clear: for different data types, Java has a different sort strategies:

  • byte, short, char their limited ranges, use the space occupied by ordering count but also 256/65536 units, as long as the quantity ordered is not particularly low (there is a sort count threshold, below this threshold do not then there is no space In other time), the count should be used to sort
  • int, long, float, double the value range is very large, not suitable for use counting sort
  • float and double and they had special circumstances:
    • NAN (not A Number The), NAN does not equal any number that does not even equal to his own
    • 0.0 +, -0.0 , float and double can not be accurately expressed decimal, we have seen are actually achieved decimal approximation, so there will be +0.0 (close to 0 positive floating point) and -0.0 (negative floating close to 0 points), in the sorting process to deal with a unified press 0, and thus the last to adjust the position of the relationship between -0.0 and +0.0
  • Object

Counting Sort

Counting sequencing is space for time sorting algorithm, which time complexity of O (n), the spatial complexity of O (m) (m is the number of possible values ​​sorted values), only in the smaller ranges should be considered when it counts sequence

(Source as a short case study)

int[] count = new int[NUM_SHORT_VALUES]; //1 << 16 = 65536,即short的可取值数量

//计数,left和right为数组要排序的范围的左界和右界
//注意,直接把
for (int i = left - 1; ++i <= right;count[a[i] - Short.MIN_VALUE]++);

//排序
for (int i = NUM_SHORT_VALUES, k = right + 1; k > left; ) {
    while (count[--i] == 0);
    short value = (short) (i + Short.MIN_VALUE);
    int s = count[i];

    do {
        a[--k] = value;
    } while (--s > 0);
}
复制代码

Sentinel inserted row

When fewer array elements, time O (n ^ 2 ^) and O (log ~ n ~) actually almost the same, and inserted row footprint rate is faster than the discharge and merge sort, so that when fewer array elements (<Strip threshold), preferentially using a power strip

Sentinel Strip Strip is optimized, the original row each taking a value interpolation traversing inserted, the inserted row sentinels take two, a larger (little endian ordering) as sentinels, when traversed Sentinel when his position, another value can be started directly from the Sentinel traverse the current position, rather than traversing over again

Only drew a static picture, if there is a good tool to draw Gif Please tell me in the comments section oh

We look at the source code:

if (leftmost) {
    //传统插排(无哨兵Sentinel)
    //遍历
    //循环向左比较(<左侧元素——换位)-直到大于左侧元素
    for (int i = left, j = i; i < right; j = ++i) {
        int ai = a[i + 1];
        while (ai < a[j]) {
            a[j + 1] = a[j];
            if (j-- == left) {
                break;
            }
        }
        a[j + 1] = ai;
    }
    
    //哨兵插排
} else {
    //如果一开始就是排好序的——直接返回
    do {
        if (left >= right) {
            return;
        }
    } while (a[++left] >= a[left - 1]);

    //以两个为单位遍历,大的元素充当哨兵,以减少小的元素循环向左比较的范围
    for (int k = left; ++left <= right; k = ++left) {
        int a1 = a[k], a2 = a[left];

        if (a1 < a2) {
            a2 = a1; a1 = a[left];
        }
        while (a1 < a[--k]) {
            a[k + 2] = a[k];
        }
        a[++k + 1] = a1;

        while (a2 < a[--k]) {
            a[k + 1] = a[k];
        }
        a[k + 1] = a2;
    }
    //确保最后一个元素被排序
    int last = a[right];

    while (last < a[--right]) {
        a[right + 1] = a[right];
    }
    a[right + 1] = last;
}
return;
复制代码

Quick drain double pivot

Highlights: double pivot fast row!

While less stable than quick drain merge sort, but it does not need to copy replication, eliminating the spatial period of the array, with less affecting the stability of the array elements will fall (> Strip threshold <quick drain threshold) priority use of fast row

Double pivot fast row in the original fast row basis, to pay more a fulcrum around together, enhance efficiency

Figure:

  1. The first step, take fulcrum

    Note: If there are five nodes equal to any two nodes to indicate that data is not uniform enough, it would have to use a single quick drain node

  2. Fast row

Source (int, for example, so long it is estimated that no one)

// Inexpensive approximation of length / 7 
// 快排阈值是286 其7分之一小于等于1/8+1/64+1
int seventh = (length >> 3) + (length >> 6) + 1;

// 获取分成7份的五个中间点
int e3 = (left + right) >>> 1; // The midpoint
int e2 = e3 - seventh;
int e1 = e2 - seventh;
int e4 = e3 + seventh;
int e5 = e4 + seventh;

// 保证中间点的元素从小到大排序
if (a[e2] < a[e1]) { 
    int t = a[e2]; a[e2] = a[e1]; a[e1] = t; }

if (a[e3] < a[e2]) { 
    int t = a[e3]; a[e3] = a[e2]; a[e2] = t;
    if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
}
if (a[e4] < a[e3]) { 
    int t = a[e4]; a[e4] = a[e3]; a[e3] = t;
    if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
                    if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
                   }
}
if (a[e5] < a[e4]) { 
    int t = a[e5]; a[e5] = a[e4]; a[e4] = t;                    
    if (t < a[e3]) { a[e4] = a[e3]; a[e3] = t;
                    if (t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
                                    if (t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
                                   }
                   }
}

// Pointers
int less  = left;  // The index of the first element of center part
int great = right; // The index before the first element of right part

//点彼此不相等——分三段快排,否则分两段
if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {
    /*
             * Use the second and fourth of the five sorted elements as pivots.
             * These values are inexpensive approximations of the first and
             * second terciles of the array. Note that pivot1 <= pivot2.
             */
    int pivot1 = a[e2];
    int 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];

    while (a[++less] < pivot1);
    while (a[--great] > pivot2);

    /*
             * Partitioning:
             *
             *   left part           center part                   right part
             * +--------------------------------------------------------------+
             * |  < pivot1  |  pivot1 <= && <= pivot2  |    ?    |  > pivot2  |
             * +--------------------------------------------------------------+
             *               ^                          ^       ^
             *               |                          |       |
             *              less                        k     great
             */
    outer:
    for (int k = less - 1; ++k <= great; ) {
        int 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);

    /*
             * If center part is too large (comprises > 4/7 of the array),
             * swap internal pivot values to ends.
             */
    if (less < e1 && e5 < great) {
        /*
                 * Skip elements, which are equal to pivot values.
                 */
        while (a[less] == pivot1) {
            ++less;
        }

        while (a[great] == pivot2) {
            --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.
                 */
        outer:
        for (int k = less - 1; ++k <= great; ) {
            int 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);

} else { // Partitioning with one pivot
    /*
             * Use the third of the five sorted elements as pivot.
             * This value is inexpensive approximation of the median.
             */
    int 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.
             */
    for (int k = less; k <= great; ++k) {
        if (a[k] == pivot) {
            continue;
        }
        int 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);
}
复制代码

Merge sort

You do not think many elements (> Quick Exhaust threshold) it must use merge, right?

wrong! Elements for a long time does have a requirement for stability of the algorithm, but if these elements can be stabilized quickly exhaust it?

Daniel JDK development is clearly consider this: whether the elements they can stabilize fast row before the merge sort of judgment:

  • If the array itself has almost lined up (can see paragraphs stitching ordered array), it is also ranked what, and patted the return on the line
  • If 33 consecutive equal elements appear - use fast row (Honestly, I did not understand why, with or without cattle to enlighten me?)
//判断结构是否适合归并排序
int[] run = new int[MAX_RUN_COUNT + 1];
int count = 0; run[0] = left;

// Check if the array is nearly sorted
for (int k = left; k < right; run[count] = k) {
    if (a[k] < a[k + 1]) { // ascending
        while (++k <= right && a[k - 1] <= a[k]);
    } else if (a[k] > a[k + 1]) { // descending
        while (++k <= right && a[k - 1] >= a[k]);
        for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {
            int t = a[lo]; a[lo] = a[hi]; a[hi] = t;
        }
    } else { 
        //连续MAX_RUN_LENGTH(33)个相等元素,使用快排
        for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k]; ) {
            if (--m == 0) {
                sort(a, left, right, true);
                return;
            }
        }
    }

    //count达到MAX_RUN_LENGTH,使用快排
    if (++count == MAX_RUN_COUNT) {
        sort(a, left, right, true);
        return;
    }
}

// Check special cases
// Implementation note: variable "right" is increased by 1.
if (run[count] == right++) { // The last run contains one element
    run[++count] = right;
} else if (count == 1) { // The array is already sorted
    return;
}
复制代码

Merge sort Source

byte odd = 0;
for (int n = 1; (n <<= 1) < count; odd ^= 1);

// Use or create temporary array b for merging
int[] b;                 // temp array; alternates with a
int ao, bo;              // array offsets from 'left'
int blen = right - left; // space needed for b
if (work == null || workLen < blen || workBase + blen > work.length) {
    work = new int[blen];
    workBase = 0;
}
if (odd == 0) {
    System.arraycopy(a, left, work, workBase, blen);
    b = a;
    bo = 0;
    a = work;
    ao = workBase - left;
} else {
    b = work;
    ao = 0;
    bo = workBase - left;
}

// Merging
for (int last; count > 1; count = last) {
    for (int k = (last = 0) + 2; k <= count; k += 2) {
        int hi = run[k], mi = run[k - 1];
        for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {
            if (q >= hi || p < mi && a[p + ao] <= a[q + ao]) {
                b[i + bo] = a[p++ + ao];
            } else {
                b[i + bo] = a[q++ + ao];
            }
        }
        run[++last] = hi;
    }
    if ((count & 1) != 0) {
        for (int i = right, lo = run[count - 1]; --i >= lo;
             b[i + bo] = a[i + ao]
            );
        run[++last] = right;
    }
    int[] t = a; a = b; b = t;
    int o = ao; ao = bo; bo = o;
}
复制代码

Regardless of the field of technology, thought the same strain, welcome to visit Orange bacteria blog article by a blog article multiple platforms OpenWrite release!

Guess you like

Origin juejin.im/post/5e12ee7bf265da5d4e274e7a