線性時間排序算法


線性時間排序算法列表

線性時間排序

Name Average Worst Memory Stable

Description

 計數排序

(Counting Sort)

n + k

n + k

n + k

Stable

 Indexes using key values.

 基數排序

(Radix Sort)

n * k

 n * k n + k Stable

 Examines individual bits of keys.

 桶排序

(Bucket Sort)

n + k

n2

n * k

Stable

 Examine bits of keys.

給定含 n 個元素的輸入序列,任何比較排序在最壞情況下都需要 Ω(n log n) 次比較來進行排序。合並排序堆排序在最壞情況下達到上界 O(n log n),它們都是漸進最優的排序算法,快速排序在平均情況下達到上界 O(n log n)。

本文介紹的三種以線性時間運行的算法:計數排序基數排序桶排序,都用非比較的一些操作來確定排序順序。因此,下界 Ω(n log n) 對它們是不適用的。

計數排序(Counting Sort)

計數排序(Counting Sort)假設 n 個輸入元素中的每一個都是介於 0 到 k 之間的整數,此處 k 為某個整數。

計數排序的基本思想就是對每一個輸入元素 x,確定出小於 x 的元素個數。有了這一信息,就可以把 x 直接放到它在最終輸出數組中的位置上。

例如:有 10 個年齡不同的人,統計出有 8 個人的年齡比 A 小,那 A 的年齡就排在第 9 位,用這個方法可以得到其他每個人的位置,也就排好了序。當然,年齡有重復時需要特殊處理(保證穩定性),這就是為什么最后要反向填充目標數組,以及將每個數字的統計減去 1 的原因。

算法描述

算法的步驟如下:

  1. 找出待排序的數組中最大和最小的元素;
  2. 統計數組中每個值為 i 的元素出現的次數,存入數組 C 的第 i 項;
  3. 對所有的計數累加(從 C 中的第一個元素開始,每一項和前一項相加);
  4. 反向填充目標數組,將每個元素 i 放在新數組的第 C(i) 項,每放一個元素就將 C(i) 減去 1;

算法復雜度

  • 最差時間復雜度 O(n + k)
  • 平均時間復雜度 O(n + k)
  • 最差空間復雜度 O(n + k)

計數排序不是比較排序,排序的速度快於任何比較排序算法。

計數排序的一個重要性質就是它是穩定的:具有相同值的元素在輸出數組中的相對次序與它們在輸入數組中的次序相同。

之所以說計數排序的穩定性非常重要,還有一個原因是因為計數排序經常用作基數排序算法的一個子過程,其穩定性對於基數排序的正確性來說非常關鍵。

代碼示例

 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = 
 6         { 
 7           5, 9, 3, 9, 10, 9, 2, 4, 13, 10
 8         };
 9 
10       OptimizedCountingSort(unsorted);
11 
12       foreach (var key in unsorted)
13       {
14         Console.Write("{0} ", key);
15       }
16 
17       Console.Read();
18     }
19 
20     static int[] CountingSort(int[] unsorted)
21     {
22       // find min and max value
23       int min = unsorted[0], max = unsorted[0];
24       for (int i = 1; i < unsorted.Length; i++)
25       {
26         if (unsorted[i] < min) min = unsorted[i];
27         else if (unsorted[i] > max) max = unsorted[i];
28       }
29 
30       // creates k buckets
31       int k = max - min + 1;
32       int[] C = new int[k];
33 
34       // calculate the histogram of key frequencies
35       for (int i = 0; i < unsorted.Length; i++)
36       {
37         C[unsorted[i] - min]++;
38       }
39 
40       // recalculate
41       C[0]--;
42       for (int i = 1; i < C.Length; i++)
43       {
44         C[i] = C[i] + C[i - 1];
45       }
46 
47       // sort the array
48       int[] B = new int[unsorted.Length];
49       for (int i = unsorted.Length - 1; i >= 0; i--)
50       {
51         // keep stable
52         B[C[unsorted[i] - min]--] = unsorted[i];
53       }
54 
55       return B;
56     }
57 
58     static void OptimizedCountingSort(int[] unsorted)
59     {
60       // find min and max value
61       int min = unsorted[0], max = unsorted[0];
62       for (int i = 1; i < unsorted.Length; i++)
63       {
64         if (unsorted[i] < min) min = unsorted[i];
65         else if (unsorted[i] > max) max = unsorted[i];
66       }
67 
68       // creates k buckets
69       int k = max - min + 1;
70       int[] C = new int[k];
71 
72       // calculate the histogram of key frequencies
73       for (int i = 0; i < unsorted.Length; i++)
74       {
75         C[unsorted[i] - min]++;
76       }
77 
78       // copy to output array,
79       // preserving order of inputs with equal keys
80       int increment = 0;
81       for (int i = min; i <= max; i++)
82       {
83         for (int j = 0; j < C[i - min]; j++)
84         {
85           // in place, may not stable if you care
86           unsorted[increment++] = i;
87         }
88       }
89     }
90   }

基數排序(Radix Sort)

基數排序(Radix Sort)是一種非比較型整數排序算法,其原理是將整數值按相同的有效位進行分組,然后在有效位區間內進行排序。

算法描述

每個元素值首先被放入一個該值的最右位所對應的桶中,桶內會保持被放入元素值最初的順序。這使得桶的數量和值的數量能夠根據其最右位建立一對一的關系。然后,通過相同的方式重復處理下一位,直到所有的位都已被處理。

  1. 獲得值的最右側的最小的位。
  2. 根據該位的值將數組內的元素值進行分組,但仍然保持元素的順序。(以此來保持算法穩定性)
  3. 重復上述分組過程,直到所有的位都已被處理。

上述第 2 步中通常可以使用桶排序(Bucket Sort)或計數排序(Counting Sort)算法,因為它們在元素較少時擁有更好的效率。

基數排序中可以選擇采用最低有效位基數排序(LSD Radix Sort:Least Significant Digit Radix Sort)或最高有效位基數排序(MSD Radix Sort:Most Significant Digit Radix Sort)。LSD 的排序方式由值的最低位也就是最右邊開始,而 MSD 則相反,由值的最高位也就是最左邊開始。

例如,如下這個無序的數列需要排序:

  170, 45, 75, 90, 802, 2, 24, 66

使用 LSD 方式從最低位開始(個位)排序的結果是:

  170, 90, 802, 2, 24, 45, 75, 66

再繼續從下一位(十位)繼續排序的結果是:

  802, 2, 24, 45, 66, 170, 75, 90

再繼續從下一位(百位)繼續排序的結果是:

  2, 24, 45, 66, 75, 90, 170, 802

算法復雜度

  • 最差時間復雜度 O(k*n)
  • 最差空間復雜度 O(k*n)

代碼示例

 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = 
 6         { 
 7           15, 19, 13, 19, 10, 33, 12, 14, 13, 10,
 8         };
 9 
10       RadixSort(unsorted);
11 
12       foreach (var key in unsorted)
13       {
14         Console.Write("{0} ", key);
15       }
16 
17       Console.Read();
18     }
19 
20     static void RadixSort(int[] unsorted)
21     {
22       // our helper array 
23       int[] t = new int[unsorted.Length];
24 
25       // number of bits our group will be long 
26       // try to set this also to 2, 8 or 16 to see if it is quicker or not
27       int r = 4;
28 
29       // number of bits of a C# int 
30       int b = 32;
31 
32       // counting and prefix arrays
33       // (note dimensions 2^r which is the number of 
34       // all possible values of a r-bit number) 
35       int[] count = new int[1 << r];
36       int[] pref = new int[1 << r];
37 
38       // number of groups 
39       int groups = (int)Math.Ceiling((double)b / (double)r);
40 
41       // the mask to identify groups 
42       int mask = (1 << r) - 1;
43 
44       // the algorithm: 
45       for (int c = 0, shift = 0; c < groups; c++, shift += r)
46       {
47         // reset count array 
48         for (int j = 0; j < count.Length; j++)
49           count[j] = 0;
50 
51         // counting elements of the c-th group 
52         for (int i = 0; i < unsorted.Length; i++)
53           count[(unsorted[i] >> shift) & mask]++;
54 
55         // calculating prefixes 
56         pref[0] = 0;
57         for (int i = 1; i < count.Length; i++)
58           pref[i] = pref[i - 1] + count[i - 1];
59 
60         // from a[] to t[] elements ordered by c-th group 
61         for (int i = 0; i < unsorted.Length; i++)
62           t[pref[(unsorted[i] >> shift) & mask]++] = unsorted[i];
63 
64         // a[]=t[] and start again until the last group 
65         t.CopyTo(unsorted, 0);
66       }
67       // a is sorted 
68     }
69   }

桶排序(Bucket Sort)

桶排序(Bucket Sort)的工作原理是將數組分解到有限數量的桶里,每個桶再分別進行排序。桶內排序有可能使用其他排序算法或是以遞歸的方式繼續使用桶排序。

算法描述

桶排序的步驟:

  1. 在數組中查找數值的最大值和最小值;
  2. 初始化一個數組當作空桶,長度為 (MaxValue - MinValue + 1)。
  3. 遍歷被排序數組,並把數值逐個放入對應的桶中。
  4. 對每個不是空的桶進行排序。
  5. 從不是空的桶里把數值再放回原來的數組中。

算法復雜度

  • 最差時間復雜度 O(n2)
  • 平均時間復雜度 O(n+k)
  • 最差空間復雜度 O(n*k)

當要被排序的數組中的數值是均勻分布時,桶排序的運行時間為線性時間 Θ(n)。桶排序不是比較排序,它不受 Ω(n log n) 下界的影響。

代碼示例

 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = 
 6         { 
 7           15, 19, 13, 19, 10, 33, 12, 14, 13, 10,
 8         };
 9 
10       BucketSort(unsorted);
11 
12       foreach (var key in unsorted)
13       {
14         Console.Write("{0} ", key);
15       }
16 
17       Console.Read();
18     }
19 
20     static void BucketSort(int[] unsorted)
21     {
22       // find the maximum and minimum values in the array
23       int max = unsorted[0]; //start with first element
24       int min = unsorted[0];
25 
26       // start from index 1
27       for (int i = 1; i < unsorted.Length; i++)
28       {
29         if (unsorted[i] < min) min = unsorted[i];
30         else if (unsorted[i] > max) max = unsorted[i];
31       }
32 
33       // create a temporary "buckets" to store the values in order
34       // each value will be stored in its corresponding index
35       // scooting everything over to the left as much as possible
36       // e.g. 34 => index at 34 - minValue
37       List<int>[] buckets = new List<int>[max - min + 1];
38 
39       // initialize the buckets
40       for (int i = 0; i < buckets.Length; i++)
41       {
42         buckets[i] = new List<int>();
43       }
44 
45       // move items to bucket
46       for (int i = 0; i < unsorted.Length; i++)
47       {
48         buckets[unsorted[i] - min].Add(unsorted[i]);
49       }
50 
51       // move items in the bucket back to the original array in order
52       int k = 0; //index for original array
53       for (int i = 0; i < buckets.Length; i++)
54       {
55         if (buckets[i].Count > 0)
56         {
57           for (int j = 0; j < buckets[i].Count; j++)
58           {
59             unsorted[k] = buckets[i][j];
60             k++;
61           }
62         }
63       }
64     }
65   }

參考資料

本篇文章《線性時間排序算法》由 Dennis Gao 發表自博客園,任何未經作者同意的爬蟲或人為轉載均為耍流氓。


免責聲明!

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



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