1. 程式人生 > >算法中的快速排序 quicksort

算法中的快速排序 quicksort

wide div 對比 nal optimize rate fse incr mce

快速排序之所以比較快,是因為相比冒泡排序,每次交換是跳躍式的。每次排序的時候設置一個基準點,將小於等於基準點的數全部放到基準點的左邊,將大於等於基準點的數全

部放到基準點的右邊。這樣在每次交換的時候就不會像冒泡排序一樣只能在相鄰的數之間進行交換,交換的距離就大得多了。因此總的比較和交換次數就少了,速度自然就提高了。

當然在最壞的情況下,仍可能是相鄰的兩個數進行了交換。因此快速排序的最差時間復雜度和冒泡排序是一樣的,都是O(N2),它的平均時間復雜度為O (NlogN)。其實快速排序是

基於一種叫做“二分”的思想。

對比了一下java中的Arrays.sort()方法,在 一本書裏面看到的簡易版更容易理解,先上傳一段代碼

public class ListTest2
{
    private static int a[] = new int[100];
    
    private static int n = 25;
    
    public static void main(String[] args)
    {
        Random random = new Random(100);
        for (int i = 0; i < n; i++)
        {
            int nextInt = random.nextInt(100);
            System.out.print(nextInt 
+ "-"); a[i] = nextInt; } System.out.println(); for (int k = 0; k < n; k++) { if (a[k] != 0) { System.out.print(a[k] + "*"); } } System.out.println(); quicksort(0, n); for (int
k = 0; k < a.length; k++) { if (a[k] != 0) { System.out.print(a[k] + " "); } } System.out.println(); } public static void quicksort(int left, int right) { System.out.println("print sorted int[]-----"); int i, j, t, temp; if (left > right) return; temp = a[left];// temp中存為基數 i = left; j = right; while (i != j) { // 順序很重要,要先從右往左找 while (a[j] >= temp && i < j) j--; // 再從左往右找 while (a[i] <= temp && i < j) i++; // 交換兩個數在數組中的位置 if (i < j)// 當哨兵i和哨兵j沒有相遇時 { t = a[i]; a[i] = a[j]; a[j] = t; } } // 最終將基準書歸位 a[left] = a[i]; a[i] = temp; quicksort(left, i - 1);// 繼續處理左邊的,遞歸規程 quicksort(i + 1, right);// 繼續處理右邊的,遞歸過程 } }

快速排序由 C. A. R. Hoare(東尼·霍爾,Charles Antony Richard Hoare)在1960 年提出,之後又有許多人做了進一步的優化,

java 版本裏面的快速排序相對復雜很多,裏面還涉及了位運算,目前還沒有具體學習,不過源碼先貼出來,方便以後學習,如果有大神願意解析一下的話,那是最好啦,好了,下面是java源碼

package test1;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;

/**
 * @author dayu
 * @version 創建時間:2017年8月24日 上午9:25:40
 *          類說明
 */
public class ListTest
{
    /**
     * The maximum number of runs in merge sort.
     */
    private static final int MAX_RUN_COUNT = 67;
    
    /**
     * The maximum length of run in merge sort.
     */
    private static final int MAX_RUN_LENGTH = 33;
    
    /**
     * If the length of an array to be sorted is less than this
     * constant, Quicksort is used in preference to merge sort.
     */
    private static final int QUICKSORT_THRESHOLD = 286;
    
    /**
     * If the length of an array to be sorted is less than this
     * constant, insertion sort is used in preference to Quicksort.
     */
    private static final int INSERTION_SORT_THRESHOLD = 47;
    
    /**
     * If the length of a byte array to be sorted is greater than this
     * constant, counting sort is used in preference to insertion sort.
     */
    private static final int COUNTING_SORT_THRESHOLD_FOR_BYTE = 29;
    
    /**
     * If the length of a short or char array to be sorted is greater
     * than this constant, counting sort is used in preference to Quicksort.
     */
    private static final int COUNTING_SORT_THRESHOLD_FOR_SHORT_OR_CHAR = 3200;
    
    public static void main(String[] args)
    {
        int a[] = {2, 4, 1, 6, 4, 8, 5, 3, 9, 11, 42, 6, 7};
        sort(a, 0, a.length - 1, null, 0, 0);
        for (int i = 0; i < a.length; i++)
        {
            System.out.print(a[i]+((i==a.length-1)?"":"*"));
        }
    }
    
    public static void sort(int[] a, int left, int right, int[] work, int workBase, int workLen)
    {
        
        // Use Quicksort on small arrays
        if (right - left < QUICKSORT_THRESHOLD)
        {
            sort(a, left, right, true);
            return;
        }
        
        /*
         * Index run[i] is the start of i-th run
         * (ascending or descending sequence).
         */
        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
            { // equal
                for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] == a[k];)
                {
                    if (--m == 0)
                    {
                        sort(a, left, right, true);
                        return;
                    }
                }
            }
            
            /*
             * The array is not highly structured,
             * use Quicksort instead of merge sort.
             */
            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;
        }
        
        // Determine alternation base for merge
        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;
        }
        
    }
    
    private static void sort(int[] a, int left, int right, boolean leftmost)
    {
        int length = right - left + 1;
        
        // Use insertion sort on tiny arrays
        if (length < INSERTION_SORT_THRESHOLD)
        {
            if (leftmost)
            {
                /*
                 * Traditional (without sentinel) insertion sort,
                 * optimized for server VM, is used in case of
                 * the leftmost part.
                 */
                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
            {
                /*
                 * Skip the longest ascending sequence.
                 */
                do
                {
                    if (left >= right)
                    {
                        return;
                    }
                } while (a[++left] >= a[left - 1]);
                
                /*
                 * Every element from adjoining part plays the role
                 * of sentinel, therefore this allows us to avoid the
                 * left range check on each iteration. Moreover, we use
                 * the more optimized algorithm, so called pair insertion
                 * sort, which is faster (in the context of Quicksort)
                 * than traditional implementation of insertion sort.
                 */
                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;
        }
        
        // Inexpensive approximation of length / 7
        int seventh = (length >> 3) + (length >> 6) + 1;
        
        /*
         * Sort five evenly spaced elements around (and including) the
         * center element in the range. These elements will be used for
         * pivot selection as described below. The choice for spacing
         * these elements was empirically determined to work well on
         * a wide variety of inputs.
         */
        int e3 = (left + right) >>> 1; // The midpoint
        int e2 = e3 - seventh;
        int e1 = e2 - seventh;
        int e4 = e3 + seventh;
        int e5 = e4 + seventh;
        
        // Sort these elements using insertion sort
        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];
            
            /*
             * Skip elements, which are less or greater than pivot values.
             */
            while (a[++less] < pivot1)
                ;
            while (a[--great] > pivot2)
                ;
            
            /*
             * 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.
             */
            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);
        }
        
    }
    
    private static void ArrayListTest(List list)
    {
        System.out.println(list);
        Collections.reverse(list);
        System.out.println(list);
    }
    
    public static void listSort()
    {
        
        int a[] = {1, 4, 2, 4, 5};
        Arrays.sort(a);
        for (int i = 0; i < a.length; i++)
        {
            System.out.print(a[a.length - i - 1] + "\t");
        }
        
        int left = 0;
        int right = a.length - 1;
        
        for (int i = left, j = i; i < right; j = ++i)
        {
            // System.out.println("i:" + i + ",j:" + j);
            int ai = a[i + 1];
            while (ai < a[j])
            {
                a[j + 1] = a[j];
                if (j-- == left)
                {
                    break;
                }
            }
            a[j + 1] = ai;
            System.out.println(ai);
        }
        System.out.println("============");
    }
    
}

可以看出大致的思維是一致的

算法中的快速排序 quicksort