八大排序算法JAVA實現(時間復雜度O(n*logn)篇)


本文講述時間復雜度為n*logn的排序算法:歸並排序、快速排序、堆排序以及希爾排序的原理、Java實現以及變形應用。

一、歸並排序

  •  原理:把兩個有序數列合並為一個有序數列。需遞歸實現。
  •  Java實現:
 1     public int[] mergeSort(int[] a, int n)
 2     {
 3         return doMergeSort(a, n, 0, n - 1);
 4     }
 5         public int[] doMergeSort(int[] a, int n, int start, int end)
 6     {
 7         int[] res = new int[n];
 8         if (n <= 1)
 9         {
10             res[0] = a[start];
11             return res;
12         }
13         // n>=2時,對其左右數組進行處理
14         int half = n / 2;
15         int leftEnd = start + half - 1;
16         int rightStart = leftEnd + 1;
17         //遞歸調用本函數,獲取有序的左數組以及右數組
18         int[] left = doMergeSort(a, half, start, leftEnd);
19         int[] right = doMergeSort(a, n - half, rightStart, end);
20         // 將左右序列合並
21         int k = 0, i = 0, j = 0;
22         while (i < half && j < n - half)
23         {//由前往后比較兩個序列,取較小值填充到res中,取值后該序列往后移動取下一個值比較
24             if (left[i] <= right[j])
25             {
26                 res[k++] = left[i++];
27             }
28             else
29             {
30                 res[k++] = right[j++];
31             }
32         }
33         // 剩余的直接放入
34         while (i < half)
35         {
36             res[k++] = left[i++];
37         }
38         while (j < n - half)
39         {
40             res[k++] = right[j++];
41         }
42         return res;
43     }            

二、快速排序

  •  原理:每一次將一個數放在一個左邊的數全部比它小,且右邊的數全部比它大的位置,然后遞歸調用函數,將其左右序列排好。這邊有一個比較好理解的做法:在數組的左邊維護一個小於區間,在遍歷的時候,發現比當前數小的數字的時候,將,擴增小於區間,並其放到小於區間內,結束后將當前數填充在小於區間后即可。
  • Java實現:
 1     public int[] quickSort(int[] a, int n)
 2     {
 3         doQuickSort(a, n, 0, n - 1);
 4         return a;
 5     }
 6     public void doQuickSort(int[] a, int n, int start, int end)
 7     {
 8         if (n > 1)
 9         {
10             int current = a[end];
11             int minLen = 0;// 小於區間的長度
12             int i = start;
13             for (; i < end; i++)
14             {
15                 if (a[i] < current)
16                 {//發現比當前數小的數,擴充小於區間
17                     int temp = a[start + minLen];
18                     a[start + minLen] = a[i];
19                     a[i] = temp;
20                     minLen++;
21                 }
22             }
23             a[end] = a[start + minLen];
24             a[start + minLen] = current;
25             //當前位置已經確定,排左右序列
26             doQuickSort(a, minLen, start, start + minLen - 1);
27             doQuickSort(a, n - minLen - 1, start + minLen + 1, end);
28         }
29     }        
  •  變形應用:三色排序練習題

有一個只由0,1,2三種元素構成的整數數組,請使用交換、原地排序而不是使用計數進行排序。給定一個只含0,1,2的整數數組A及它的大小,請返回排序后的數組。保證數組大小小於等於500。
測試樣例:
[0,1,1,0,2,2],6
返回:[0,0,1,1,2,2]

解析:運用快排的原理。用數字1來處理,在數組左右各維護一個小於區間和大於區間。

 

三、堆排序

  • 原理:維護一個大根堆(小根堆同理),即維護一棵二叉樹,該數子節點永遠比父節點小。每次在大根堆中取出根,根為此時待排序列最大值,放在待排序列最后,然后調整大根堆,重復上訴過程即可。
  • Java實現:博主不太會插圖,關於大小根堆的調整細節可自行百度。原理總結來說是從最后一個非葉子節點開始,往前調整。設當前調整的非葉子節點為n,選舉n,n的左,n的右三個節點的最大值作為父節點。且每次調整了靠前的非葉子節點的值后,可能會破壞下面的數的大根堆規則,需要再次調整。嗯我覺得我並沒有講清楚,百度看看圖就好。
    public int[] heapSort(int[] a, int n)
    {
        for (int len = n; len > 0; len--)
        {// len為構建的堆的大小
            for (int i = len / 2 - 1; i >= 0; i--)
            {// 從后往前遍歷非葉子節點
                while (2 * i + 1 < len)
                {
                    int j = 2 * i + 1;// 左節點序號
                    if (j + 1 < len && a[j] < a[j + 1])
                    {
                        j++;
                    }
                    if (a[i] < a[j])
                    {
                        int temp = a[j];
                        a[j] = a[i];
                        a[i] = temp;
                        i = j;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            int temp = a[len - 1];
            a[len - 1] = a[0];
            a[0] = temp;
        }
        return a;
    }
  • 變形應用:常規應用如在1000+個數中找出最大的10個數之類的。

已知一個幾乎有序的數組,幾乎有序是指,如果把數組排好順序的話,每個元素移動的距離可以不超過k,並且k相對於數組來說比較小。請選擇一個合適的排序算法針對這個數據進行排序。 給定一個int數組A,同時給定A的大小n和題意中的k,請返回排序后的數組。

測試樣例: [2,1,4,3,6,5,8,7,10,9],10,2

返回:[1,2,3,4,5,6,7,8,9,10]

解析:維護一個大小為k的小根堆,每次調整完這個小根堆后,最小值就會出現在第一位,此時移除第一位,添加后一位數進來,繼續調整這個小根堆即可。可以想象為一個從前往后移動的滑動窗口,滑動窗口中是一個小根堆。

四、希爾排序

  • 原理:變形后的插入排序,每個數只與它前面固定步長的倍數的位置進行比對。如:步長step,當前數與它前面step,step*2,step*3....位置進行比較,插入到合適的位置。
  • Java實現:
    public int[] shellSort(int[] a, int n)
    {
        // 步長選擇
        for (int k = 1, step = 10; step > 1; k++)
        {
            step = n / (2 * k);
            for (int i = step; i < n; i++)
            {
                if (a[i] < a[i - step])
                {
                    int temp = a[i];
                    a[i] = a[i - step];
                    a[i - step] = temp;
                    int pre = i - step;
                    while (pre - step > 0)
                    {
                        if (a[pre] < a[pre - step])
                        {
                            temp = a[pre];
                            a[pre] = a[pre - step];
                            a[pre - step] = temp;
                            pre -= step;
                        }
                        else
                        {
                            break;
                        }
                    }
                }
            }
        }
        return a;
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM