比較排序算法分類


比較排序(Comparison Sort)經過對數組中的元素進行比較來實現排序。html

比較排序算法(Comparison Sorts)node

Category算法

Nameshell

Bestapi

Average Worst Memory Stability

 插入排序 數組

(Insertion Sorts)服務器

 

 插入排序數據結構

(Insertion Sort)app

n框架

n2

n2

1

Stable

 希爾排序

(Shell Sort)

n

n log2 n

n log2 n

1

Not Stable

 交換排序

(Exchange Sorts )

 

 

 

 快速排序

(Quick Sort)

 n log n

n log n

n2

log n

Not Stable

 冒泡排序

(Bubble Sort)

n

n2

n2

1

Stable

 雞尾酒排序

(Cocktail Sort)

n

n2

n2

1

Stable

 奇偶排序

(Odd-Even Sort)

n

n2

n2

1

Stable

 選擇排序

(Selection Sorts)

 

 選擇排序

(Selection Sort)

 n2

n2

n2

1

Not Stable

 堆排序

(Heap Sort)

 n log n

n log n

n log n

1

Not Stable

 合併排序

(Merge Sorts)

 合併排序

(Merge Sort)

 n

n log n

n log n

n

Stable

 混合排序

(Hybrid Sorts)

 內省排序

(Introspective Sort)

 n log n

n log n

n log n

log n

Not Stable

注:關於 Memory,若是算法爲 "in place" 排序,則僅須要 O(1) 內存;有時對於額外的 O(log(n)) 內存也能夠稱爲 "in place"。

注:Microsoft .NET Framework 中 Array.Sort 方法的實現使用了內省排序(Introspective Sort)算法

Stable 與 Not Stable 的比較

穩定排序算法會將相等的元素值維持其相對次序。若是一個排序算法是穩定的,當有兩個有相等的元素值 R 和 S,且在本來的列表中 R 出如今 S 以前,那麼在排序過的列表中 R 也將會是在 S 以前。

O(n2) 與 O(n*logn) 的比較

合併排序和堆排序在最壞狀況下達到上界 O(n*logn),快速排序在平均狀況下達到上界 O(n*logn)。對於比較排序算法,咱們都能給出 n 個輸入的數值,使算法以 Ω(n*logn) 時間運行。

注:有關算法複雜度,可參考文章《算法複雜度分析》。有關經常使用數據結構的複雜度,可參考文章《經常使用數據結構及複雜度》。

冒泡排序(Bubble Sort)

算法描述

重複地比較要排序的數列,一次比較兩個元素,若是後者較小則與前者交換元素。

  1. 比較相鄰的元素,若是前者比後者大,則交換兩個元素。
  2. 對每一對相鄰元素做一樣的工做,從開始第一對到結尾的最後一對。
  3. 針對全部的元素重複以上的步驟,除了最後一個。

算法複雜度

冒泡排序對 n 個元素須要 O(n2) 的比較次數,且能夠原地排序。冒泡排序僅適用於對於含有較少元素的數列進行排序。

  • 最差時間複雜度 O(n2)
  • 平均時間複雜度 O(n2)
  • 最優時間複雜度 O(n)
  • 最差空間複雜度 O(n),輔助空間 O(1)

示例代碼

複製代碼
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = { 4, 1, 5, 2, 6, 3, 7, 9, 8 };
 6 
 7       OptimizedBubbleSort(unsorted);
 8 
 9       foreach (var key in unsorted)
10       {
11         Console.Write("{0} ", key);
12       }
13 
14       Console.Read();
15     }
16 
17     static void BubbleSort(int[] unsorted)
18     {
19       for (int i = 0; i < unsorted.Length; i++)
20       {
21         for (int j = 0; j < unsorted.Length - 1 - i; j++)
22         {
23           if (unsorted[j] > unsorted[j + 1])
24           {
25             int temp = unsorted[j];
26             unsorted[j] = unsorted[j + 1];
27             unsorted[j + 1] = temp;
28           }
29         }
30       }
31     }
32 
33     static void OptimizedBubbleSort(int[] unsorted)
34     {
35       int exchange = unsorted.Length - 1;
36       while (exchange > 0)
37       {
38         int lastExchange = exchange;
39         exchange = 0;
40 
41         for (int i = 0; i < lastExchange; i++)
42         {
43           if (unsorted[i] > unsorted[i + 1])
44           {
45             int temp = unsorted[i];
46             unsorted[i] = unsorted[i + 1];
47             unsorted[i + 1] = temp;
48 
49             exchange = i;
50           }
51         }
52       }
53     }
54   }
複製代碼

雞尾酒排序(Cocktail Sort)

算法描述

雞尾酒排序,也就是雙向冒泡排序(Bidirectional Bubble Sort),是冒泡排序的一種變形。此算法與冒泡排序的不一樣處在於排序時是以雙向在序列中進行排序。若是序列中的大部分元素已經排序好時,能夠獲得比冒泡排序更好的性能。

算法複雜度

  • 最差時間複雜度 O(n2)
  • 平均時間複雜度 O(n2)
  • 最優時間複雜度 O(n)
  • 最差空間複雜度 О(1)

代碼示例

複製代碼
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = { 4, 1, 5, 2, 6, 3, 7, 9, 8 };
 6 
 7       OptimizedCocktailSort(unsorted);
 8 
 9       foreach (var key in unsorted)
10       {
11         Console.Write("{0} ", key);
12       }
13 
14       Console.Read();
15     }
16 
17     static void CocktailSort(int[] unsorted)
18     {
19       for (int i = 0; i < unsorted.Length / 2; i++)
20       {
21         // move the larger to right side
22         for (int j = i; j + 1 < unsorted.Length - i; j++)
23         {
24           if (unsorted[j] > unsorted[j + 1])
25           {
26             int temp = unsorted[j];
27             unsorted[j] = unsorted[j + 1];
28             unsorted[j + 1] = temp;
29           }
30         }
31 
32         // move the smaller to left side
33         for (int j = unsorted.Length - i - 1; j > i; j--)
34         {
35           if (unsorted[j - 1] > unsorted[j])
36           {
37             int temp = unsorted[j - 1];
38             unsorted[j - 1] = unsorted[j];
39             unsorted[j] = temp;
40           }
41         }
42       }
43     }
44 
45     static void OptimizedCocktailSort(int[] unsorted)
46     {
47       bool swapped = false;
48       int start = 0;
49       int end = unsorted.Length - 1;
50       do
51       {
52         swapped = false;
53 
54         // move the larger to right side
55         for (int i = start; i < end; i++)
56         {
57           if (unsorted[i] > unsorted[i + 1])
58           {
59             int temp = unsorted[i];
60             unsorted[i] = unsorted[i + 1];
61             unsorted[i + 1] = temp;
62 
63             swapped = true;
64           }
65         }
66 
67         // we can exit the outer loop here if no swaps occurred.
68         if (!swapped) break;
69         swapped = false;
70         end = end - 1;
71 
72         // move the smaller to left side
73         for (int j = end; j > start; j--)
74         {
75           if (unsorted[j - 1] > unsorted[j])
76           {
77             int temp = unsorted[j];
78             unsorted[j] = unsorted[j - 1];
79             unsorted[j - 1] = temp;
80 
81             swapped = true;
82           }
83         }
84 
85         start = start + 1;
86       }
87       while (swapped);
88     }
89   }
複製代碼

奇偶排序(Odd-Even Sort)

奇偶排序經過比較數組中相鄰的(奇-偶)位置元素,若是該奇偶元素對是錯誤的順序(前者大於後者),則交換元素。而後再針對全部的(偶-奇)位置元素進行比較。如此交替進行下去

 

算法複雜度

  • 最差時間複雜度 O(n2)
  • 平均時間複雜度 O(n2)
  • 最優時間複雜度 O(n)
  • 最差空間複雜度 О(1)

代碼示例

複製代碼
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = { 4, 1, 5, 2, 6, 3, 7, 9, 8 };
 6 
 7       OptimizedOddEvenSort(unsorted);
 8 
 9       foreach (var key in unsorted)
10       {
11         Console.Write("{0} ", key);
12       }
13 
14       Console.Read();
15     }
16 
17     static void OddEvenSort(int[] unsorted)
18     {
19       for (int i = 0; i < unsorted.Length; ++i)
20       {
21         if (i % 2 > 0)
22         {
23           for (int j = 2; j < unsorted.Length; j += 2)
24           {
25             if (unsorted[j] < unsorted[j - 1])
26             {
27               int temp = unsorted[j - 1];
28               unsorted[j - 1] = unsorted[j];
29               unsorted[j] = temp;
30             }
31           }
32         }
33         else
34         {
35           for (int j = 1; j < unsorted.Length; j += 2)
36           {
37             if (unsorted[j] < unsorted[j - 1])
38             {
39               int temp = unsorted[j - 1];
40               unsorted[j - 1] = unsorted[j];
41               unsorted[j] = temp;
42             }
43           }
44         }
45       }
46     }
47 
48     static void OptimizedOddEvenSort(int[] unsorted)
49     {
50       bool swapped = true;
51       int start = 0;
52 
53       while (swapped || start == 1)
54       {
55         swapped = false;
56 
57         for (int i = start; i < unsorted.Length - 1; i += 2)
58         {
59           if (unsorted[i] > unsorted[i + 1])
60           {
61             int temp = unsorted[i];
62             unsorted[i] = unsorted[i + 1];
63             unsorted[i + 1] = temp;
64 
65             swapped = true;
66           }
67         }
68 
69         if (start == 0) start = 1;
70         else start = 0;
71       }
72     }
73   }
複製代碼

快速排序(Quick Sort)

快速排序使用分治法(Divide-and-Conquer)策略將一個數列分紅兩個子數列並使用遞歸來處理。

好比有以下這個 10 個數字,[13, 81, 92, 42, 65, 31, 57, 26, 75, 0]。

隨機選擇一個數做爲中間的元素,例如選擇 65。

這樣數組就被 65 分紅了兩部分,左邊的都小於 65,右邊的都大於 65。

而後分別對左右兩邊的子數組按照相同的方式進行排序,並最終排序完畢。

算法描述

  1. 從數列中挑出一個元素,稱爲 "主元"(pivot)。
  2. 從新排序數列,全部元素比主元小的擺放在主元前面,全部元素比主元值大的擺在主元的後面(相同的數能夠到任一邊)。這個稱爲分區(partition)操做。在分區退出以後,該主元就處於數列的中間位置。
  3. 遞歸地(recursively)把小於主元值元素的子數列和大於主元值元素的子數列排序。

遞歸的最底部情形,是數列的大小是 0 或 1 ,也就是老是被排序好的情況。這樣一直遞歸下去,直到算法退出。

下面的過程實現快速排序,調用 QUICKSORT(A, 1, length[A])。

1 QUICKSORT(A, p, r)
2     if p < r
3         then q <- PARTITION(A, p, r)
4             QUICKSORT(A, p, q - 1)
5             QUICKSORT(A, q + 1, r)

快速排序算法的關鍵是 Partition 過程,它對子數組進行就是重排。

複製代碼
1 PARTITION(A, p, r)
2     x <- A[r]
3     i <- p - 1
4     for j <- p to r - 1
5         do if A[j] <= x
6             then i <- i + 1
7                 exchange A[i] <-> A[j]
8     exchange A[i + 1] <-> A[r]
9     return  i + 1
複製代碼

算法複雜度

  • 最差時間複雜度 O(n2)
  • 平均時間複雜度 O(n*log n)
  • 最優時間複雜度 O(n*log n)
  • 最差空間複雜度 根據實現的方式不一樣而不一樣 O(n) 輔助空間 O(log n)

快速排序的運行時間與劃分是否對稱有關,然後者又與選擇了哪個元素來進行劃分有關。若是劃分是對稱的,那麼快速排序從漸進意義上來說,就與合併算法同樣快;若是劃分是不對稱的,那麼從漸進意義上來說,就與插入排序同樣慢。

快速排序的平均運行時間與其最佳狀況運行時間很接近,而不是很是接近於最差狀況運行時間。

QUICKSORT 的運行時間是由花在過程 PARTITION 上的時間所決定的。每當 PARTITION 過程被調用時,就要選出一個 Pivot 元素。後續對 QUICKSORT 和 PARTITION 的各次遞歸調用中,都不會包含該元素。因而,在快速排序算法的整個執行過程當中,至多隻可能調用 PARTITION 過程 n 次。

快速排序的隨機化版本

快速排序的隨機化版本是對足夠大的輸入的理想選擇。

RANDOMIZED-QUICKSORT 的平均運行狀況是 O(n lg n),若是在遞歸的每一層上,RANDOMIZED-PARTITION 所做出的劃分使任意固定量的元素偏向劃分的某一邊,則算法的遞歸樹深度爲 Θ(lg n),且在每一層上所作的工做量都爲 O(n)。

1 RANDOMIZED-PARTITION(A, p, r)
2     i <- RANDOM(p, r)
3     exchange A[r] <-> A[i]
4     return PARTITION(A, p, r)

算法比較

快速排序是二叉查找樹的一個空間優化版本。但其不是循序地把數據項插入到一個顯式的樹中,而是由快速排序組織這些數據項到一個由遞歸調用所隱含的樹中。這兩個算法徹底地產生相同的比較次數,可是順序不一樣。

快速排序的最直接競爭者是堆排序(Heap Sort)。堆排序一般會慢於原地排序的快速排序,其最壞狀況的運行時間老是 O(n log n) 。快速排序一般狀況下會比較快,但仍然有最壞狀況發生的機會。

快速排序也會與合併排序(Merge Sort)競爭。合併排序的特色是最壞狀況有着 O(n log n) 運行時間的優點。不像快速排序或堆排序,合併排序是一個穩定排序算法,而且很是的靈活,其設計能夠應用於操做鏈表,或大型鏈式存儲等,例如磁盤存儲或網路附加存儲等。儘管快速排序也能夠被重寫使用在鏈表上,但對於基準的選擇老是個問題。合併排序的主要缺點是在最佳狀況下須要 O(n) 額外的空間,而快速排序的原地分區和尾部遞歸僅使用 O(log n) 的空間。

代碼示例

複製代碼
  1   class Program
  2   {
  3     static bool isPrintArrayEnabled = false;
  4 
  5     static void Main(string[] args)
  6     {
  7       int[] smallSeed = { 4, 1, 5, 2, 6, 3, 7, 9, 8, 0 };
  8 
  9       MeasureQuickSort(smallSeed, 1000000);
 10       MeasureRandomizedQuickSort(smallSeed, 1000000);
 11       MeasureOptimizedQuickSorts(smallSeed, 1000000);
 12 
 13       Console.Read();
 14     }
 15 
 16     static void MeasureQuickSort(int[] smallSeed, int arrayLength)
 17     {
 18       int[] unsorted = GenerateBigUnsortedArray(smallSeed, arrayLength);
 19 
 20       Stopwatch watch = Stopwatch.StartNew();
 21 
 22       QuickSort(unsorted, 0, unsorted.Length - 1);
 23 
 24       watch.Stop();
 25 
 26       Console.WriteLine(
 27         "ArrayLength[{0}], QuickSort ElapsedMilliseconds[{1}]",
 28         unsorted.Length, watch.ElapsedMilliseconds);
 29 
 30       PrintArray(unsorted);
 31     }
 32 
 33     static void MeasureRandomizedQuickSort(int[] smallSeed, int arrayLength)
 34     {
 35       int[] unsorted = GenerateBigUnsortedArray(smallSeed, arrayLength);
 36 
 37       Stopwatch watch = Stopwatch.StartNew();
 38 
 39       RandomizedQuickSort(unsorted, 0, unsorted.Length - 1);
 40 
 41       watch.Stop();
 42 
 43       Console.WriteLine(
 44         "ArrayLength[{0}], RandomizedQuickSort ElapsedMilliseconds[{1}]",
 45         unsorted.Length, watch.ElapsedMilliseconds);
 46 
 47       PrintArray(unsorted);
 48     }
 49 
 50     static void QuickSort(int[] unsorted, int left, int right)
 51     {
 52       // left  爲子數列的最左邊元素
 53       // right 爲子數列的最右邊元素
 54       if (!(left < right)) return;
 55 
 56       // Partition: 
 57       // 全部元素比主元值小的擺放在主元的左邊,
 58       // 全部元素比主元值大的擺放在主元的右邊
 59       int pivotIndex = Partition(unsorted, left, right);
 60 
 61       // Recursively:
 62       // 分別排列小於主元的值和大於主元的值的子數列
 63       // 主元無需參加下一次排序
 64       QuickSort(unsorted, left, pivotIndex - 1);
 65       QuickSort(unsorted, pivotIndex + 1, right);
 66     }
 67 
 68     static int Partition(int[] unsorted, int left, int right)
 69     {
 70       int pivotIndex = right;
 71 
 72       // 哨兵
 73       int sentinel = unsorted[right];
 74 
 75       // 子數組長度爲 right - left + 1
 76       int i = left - 1;
 77       for (int j = left; j <= right - 1; j++)
 78       {
 79         if (unsorted[j] <= sentinel)
 80         {
 81           i++;
 82           Swap(unsorted, i, j);
 83         }
 84       }
 85 
 86       Swap(unsorted, i + 1, pivotIndex);
 87 
 88       return i + 1;
 89     }
 90 
 91     static void RandomizedQuickSort(int[] unsorted, int left, int right)
 92     {
 93       // left  爲子數列的最左邊元素
 94       // right 爲子數列的最右邊元素
 95       if (!(left < right)) return;
 96 
 97       // Partition: 
 98       // 全部元素比主元值小的擺放在主元的左邊,
 99       // 全部元素比主元值大的擺放在主元的右邊
100       int pivotIndex = RandomizedPartition(unsorted, left, right);
101 
102       // Recursively:
103       // 分別排列小於主元的值和大於主元的值的子數列
104       // 主元無需參加下一次排序
105       RandomizedQuickSort(unsorted, left, pivotIndex - 1);
106       RandomizedQuickSort(unsorted, pivotIndex + 1, right);
107     }
108 
109     static int RandomizedPartition(int[] unsorted, int left, int right)
110     {
111       int i = random.Next(left, right);
112       Swap(unsorted, i, right);
113       return Partition(unsorted, left, right);
114     }
115 
116     static void Swap(int[] unsorted, int i, int j)
117     {
118       int temp = unsorted[i];
119       unsorted[i] = unsorted[j];
120       unsorted[j] = temp;
121     }
122 
123     static void MeasureOptimizedQuickSorts(int[] smallSeed, int arrayLength)
124     {
125       foreach (var pivotSelection in
126         Enum.GetValues(typeof(QuickSortPivotSelectionType)))
127       {
128         int[] unsorted = GenerateBigUnsortedArray(smallSeed, arrayLength);
129 
130         Stopwatch watch = Stopwatch.StartNew();
131 
132         OptimizedQuickSort(unsorted, 0, unsorted.Length - 1,
133           (QuickSortPivotSelectionType)pivotSelection);
134 
135         watch.Stop();
136 
137         Console.WriteLine(
138           "ArrayLength[{0}], "
139           + "QuickSortPivotSelectionType[{1}], "
140           + "ElapsedMilliseconds[{2}]",
141           unsorted.Length,
142           (QuickSortPivotSelectionType)pivotSelection,
143           watch.ElapsedMilliseconds);
144 
145         PrintArray(unsorted);
146       }
147     }
148 
149     static int[] GenerateBigUnsortedArray(int[] smallSeed, int arrayLength)
150     {
151       int[] bigSeed = new int[100];
152       for (int i = 0; i < bigSeed.Length; i++)
153       {
154         bigSeed[i] =
155           smallSeed[i % smallSeed.Length]
156             + i / smallSeed.Length * 10;
157       }
158 
159       int[] unsorted = new int[arrayLength];
160       for (int i = 0; i < unsorted.Length / bigSeed.Length; i++)
161       {
162         Array.Copy(bigSeed, 0, unsorted, i * bigSeed.Length, bigSeed.Length);
163       }
164 
165       return unsorted;
166     }
167 
168     static void OptimizedQuickSort(int[] unsorted, int left, int right,
169       QuickSortPivotSelectionType pivotSelection)
170     {
171       // left  爲子數列的最左邊元素
172       // right 爲子數列的最右邊元素
173       if (!(left < right)) return;
174 
175       // Partition: 
176       // 全部元素比主元值小的擺放在主元的左邊,
177       // 全部元素比主元值大的擺放在主元的右邊
178       Tuple<int, int> pivotPair =
179         OptimizedPartition(unsorted, left, right, pivotSelection);
180 
181       // Recursively:
182       // 分別排列小於主元的值和大於主元的值的子數列
183       // 主元無需參加下一次排序
184       OptimizedQuickSort(unsorted, left, pivotPair.Item1 - 1, pivotSelection);
185       OptimizedQuickSort(unsorted, pivotPair.Item2 + 1, right, pivotSelection);
186     }
187 
188     static Tuple<int, int> OptimizedPartition(
189       int[] unsorted, int left, int right,
190       QuickSortPivotSelectionType pivotSelection)
191     {
192       int pivotIndex = SelectPivot(unsorted, left, right, pivotSelection);
193       int sentinel = unsorted[pivotIndex];
194 
195       // 子數組長度爲 right - left + 1
196       int i = left - 1;
197       int j = right + 1;
198       while (true)
199       {
200         while (unsorted[++i] < sentinel) ;
201         while (unsorted[--j] > sentinel) ;
202         if (i >= j) break;
203 
204         // 在主元左側找到一個大於主元值的位置 i,
205         // 在主元右側找到一個小於主元值的位置 j,
206         // 交換兩個值
207         Swap(unsorted, i, j);
208       }
209 
210       return new Tuple<int, int>(i, j);
211     }
212 
213     static int SelectPivot(int[] unsorted, int left, int right,
214       QuickSortPivotSelectionType pivotSelection)
215     {
216       switch (pivotSelection)
217       {
218         case QuickSortPivotSelectionType.FirstAsPivot:
219           return left;
220         case QuickSortPivotSelectionType.MiddleAsPivot:
221           return (left + right) / 2;
222         case QuickSortPivotSelectionType.LastAsPivot:
223           return right;
224         case QuickSortPivotSelectionType.RandomizedPivot:
225           {
226             // 在區間內隨機選擇位置
227             if (right - left > 1)
228             {
229               return random.Next(left, right);
230             }
231             else
232             {
233               return left;
234             }
235           }
236         case QuickSortPivotSelectionType.BalancedPivot:
237           {
238             // 選擇起始、中間和結尾位置中的中位數
239             int leftValue = unsorted[left];
240             int middleValue = unsorted[(left + right) / 2];
241             int rightValue = unsorted[right];
242 
243             if (leftValue < middleValue)
244             {
245               if (middleValue < rightValue)
246               {
247                 return (left + right) / 2;
248               }
249               else
250               {
251                 return right;
252               }
253             }
254             else
255             {
256               if (leftValue < rightValue)
257               {
258                 return left;
259               }
260               else
261               {
262                 return right;
263               }
264             }
265           }
266       }
267 
268       return (left + right) / 2;
269     }
270 
271     static void PrintArray(int[] unsorted)
272     {
273       if (!isPrintArrayEnabled) return;
274 
275       foreach (var item in unsorted)
276       {
277         Console.Write("{0} ", item);
278       }
279       Console.WriteLine();
280     }
281 
282     static Random random = new Random(new Guid().GetHashCode());
283 
284     enum QuickSortPivotSelectionType
285     {
286       FirstAsPivot,
287       MiddleAsPivot,
288       LastAsPivot,
289       RandomizedPivot,
290       BalancedPivot,
291     }
292   }
複製代碼

代碼運行後結果

選擇排序(Selection Sort)

算法原理

首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,而後,再從剩餘未排序元素中繼續尋找最小(大)元素,而後放到已排序序列的末尾。以此類推,直到全部元素均排序完畢。

算法複雜度

選擇排序的交換操做介於 0 和 (n-1) 次之間。選擇排序的比較操做爲 n(n-1)/2 次之間。選擇排序的賦值操做介於 0 和 3(n-1) 次之間。

比較次數 O(n2),比較次數與關鍵字的初始狀態無關,總的比較次數 N = (n-1)+(n-2)+...+1 = n*(n-1)/2。交換次數 O(n),最好狀況是,已經有序,交換 0 次;最壞狀況是,逆序,交換 n-1 次。交換次數比冒泡排序較少,因爲交換所需 CPU 時間比比較所需的 CPU 時間多,n 值較小時,選擇排序比冒泡排序快。

原地操做幾乎是選擇排序的惟一優勢,當空間複雜度(space complexity)要求較高時,能夠考慮選擇排序,實際適用的場合很是罕見。

  • 最差時間複雜度 О(n²)
  • 平均時間複雜度 О(n²)
  • 最優時間複雜度 О(n²)
  • 最差空間複雜度 О(n),輔助空間 O(1)

代碼示例

複製代碼
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = { 4, 1, 5, 2, 6, 3, 7, 9, 8 };
 6 
 7       SelectionSort(unsorted);
 8 
 9       foreach (var key in unsorted)
10       {
11         Console.Write("{0} ", key);
12       }
13 
14       Console.Read();
15     }
16 
17     static void SelectionSort(int[] unsorted)
18     {
19       // advance the position through the entire array
20       // could do i < n-1 because single element is also min element
21       for (int i = 0; i < unsorted.Length - 1; i++)
22       {
23         // find the min element in the unsorted a[i .. n-1]
24         // assume the min is the first element
25         int min = i;
26 
27         // test against elements after i to find the smallest
28         for (int j = i + 1; j < unsorted.Length; j++)
29         {
30           // if this element is less, then it is the new minimum
31           if (unsorted[j] < unsorted[min])
32           {
33             // found new minimum, remember its index
34             min = j;
35           }
36         }
37 
38         // swap
39         if (min != i)
40         {
41           int temp = unsorted[i];
42           unsorted[i] = unsorted[min];
43           unsorted[min] = temp;
44         }
45       }
46     }
47   }
複製代碼

插入排序(Insertion Sort)

算法原理

對於未排序數據,在已排序序列中從後向前掃描,找到相應位置,將位置後的已排序數據逐步向後挪位,將新元素插入到該位置。

算法描述

  1. 從第一個元素開始,該元素能夠認爲已經被排序;
  2. 取出下一個元素,在已經排序的元素序列中從後向前掃描;
  3. 若是該元素(已排序)大於新元素,將該元素移到下一位置;
  4. 重複步驟 3,直到找到已排序的元素小於或者等於新元素的位置;
  5. 將新元素插入到該位置後;
  6. 重複步驟 2~5;

算法複雜度

  • In-Place 原地排序(即只須要用到 O(1) 的額外空間)
  • 最差時間複雜度 O(n2)
  • 平均時間複雜度 O(n2)
  • 最優時間複雜度 O(n)
  • 最差空間複雜度 O(n),輔助空間 O(1)

插入排序算法的內循環是緊密的,對小規模輸入來講是一個快速的原地排序算法。

示例代碼

複製代碼
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = { 4, 1, 5, 2, 6, 3, 7, 9, 8 };
 6 
 7       InsertionSort(unsorted);
 8 
 9       foreach (var key in unsorted)
10       {
11         Console.Write("{0} ", key);
12       }
13 
14       Console.Read();
15     }
16 
17     static void InsertionSort(int[] unsorted)
18     {
19       for (int i = 1; i < unsorted.Length; i++)
20       {
21         if (unsorted[i - 1] > unsorted[i])
22         {
23           int key = unsorted[i];
24           int j = i;
25           while (j > 0 && unsorted[j - 1] > key)
26           {
27             unsorted[j] = unsorted[j - 1];
28             j--;
29           }
30           unsorted[j] = key;
31         }
32       }
33     }
34   }
複製代碼

希爾排序(Shell Sort)

希爾排序是插入排序的一種更高效的改進版本,其基於插入排序的如下兩個特色提出改進方法:

  • 插入排序在對幾乎已經排序的數據操做時,效率高,能夠達到線性時間。
  • 插入排序通常來講是低效的,其每次只能將數據移動一位。

算法描述

希爾排序經過將比較的所有元素分爲幾個區域來提高插入排序的性能。這樣可讓一個元素一次性地朝最終位置前進一大步。而後算法再取愈來愈小的步長進行排序,算法的最後一步就是普通的插入排序,可是到了這步,需排序的數據幾乎是已排好的了,此時插入排序較快。

假設有一個很小的數據在一個已按升序排好序的數組的末端。若是用複雜度爲 O(n2) 的排序(冒泡排序或插入排序),可能會進行 n 次的比較和交換才能將該數據移至正確位置。而希爾排序會用較大的步長移動數據,因此小數據只需進行少許比較和交換便可移到正確位置。

步長序列(Gap Sequences)

步長的選擇是希爾排序的重要部分。只要最終步長爲 1 任何步長串行均可以工做。算法最開始以必定的步長進行排序。而後會繼續以必定步長進行排序,最終算法以步長爲 1 進行排序。當步長爲 1 時,算法變爲插入排序,這就保證了數據必定會被排序。

已知的最好步長串行是由 Sedgewick 提出的 (1, 5, 19, 41, 109,...),該步長的項來自 9 * 4^i - 9 * 2^i + 1 和 4^i - 3 * 2^i + 1 這兩個算式。這項研究也代表 "比較在希爾排序中是最主要的操做,而不是交換。" 用這樣步長串行的希爾排序比插入排序和堆排序都要快,甚至在小數組中比快速排序還快,可是在涉及大量數據時希爾排序仍是比快速排序慢。

算法複雜度

  • 最差時間複雜度 O(nlog2 n)
  • 平均時間複雜度 依賴於步長間隔 O(nlog2 n)
  • 最優時間複雜度 O(nlogn)
  • 最差空間複雜度 O(n),輔助空間 O(1)

代碼示例

複製代碼
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = { 4, 1, 5, 2, 6, 3, 7, 9, 8 };
 6 
 7       ShellSort(unsorted);
 8 
 9       foreach (var key in unsorted)
10       {
11         Console.Write("{0} ", key);
12       }
13 
14       Console.Read();
15     }
16 
17     static void ShellSort(int[] unsorted)
18     {
19       int gap = (int)Math.Ceiling(unsorted.Length / 2D);
20 
21       // start with the largest gap and work down to a gap of 1 
22       while (gap > 0)
23       {
24         // do a gapped insertion sort for this gap size.
25         // the first gap elements a[0..gap-1] are already in gapped order
26         // keep adding one more element until the entire array is gap sorted 
27         for (int i = 0; i < unsorted.Length; i++)
28         {
29           // add a[i] to the elements that have been gap sorted
30           // save a[i] in temp and make a hole at position i
31           int j = i;
32           int temp = unsorted[i];
33 
34           // shift earlier gap-sorted elements up 
35           // until the correct location for a[i] is found
36           while (j >= gap && unsorted[j - gap] > temp)
37           {
38             unsorted[j] = unsorted[j - gap];
39             j = j - gap;
40           }
41 
42           // put temp (the original a[i]) in its correct location
43           unsorted[j] = temp;
44         }
45 
46         // change gap
47         gap = (int)Math.Floor(0.5 + gap / 2.2);
48       }
49     }
50   }
複製代碼

合併排序(Merge Sort)

合併排序是分治法的典型應用。

分治法(Divide-and-Conquer):將原問題劃分紅 n 個規模較小而結構與原問題類似的子問題;遞歸地解決這些問題,而後再合併其結果,就獲得原問題的解。

分治模式在每一層上都有三個步驟:

  1. 分解(Divide):將原問題分解成一系列子問題;
  2. 解決(Conquer):遞歸地解決各個子問題。若子問題足夠小,則直接求解;
  3. 合併(Combine):將子問題的結果合併成原問題的解。

合併排序算法徹底依照了上述模式:

  1. 分解:將 n 個元素分紅各含 n/2 個元素的子序列;
  2. 解決:用合併排序法對兩個子序列遞歸地排序;
  3. 合併:合併兩個已排序的子序列以獲得排序結果。

算法描述

  1. 申請空間,使其大小爲兩個已經排序序列之和,該空間用來存放合併後的序列;
  2. 設定兩個指針,最初位置分別爲兩個已經排序序列的起始位置;
  3. 比較兩個指針所指向的元素,選擇相對小的元素放入到合併空間,並移動指針到下一位置;
  4. 重複步驟 3 直到某一指針到達序列尾;
  5. 將另外一序列剩下的全部元素直接複製到合併序列尾;

算法複雜度

  • 最差時間複雜度 Θ(n*logn)
  • 平均時間複雜度 Θ(n*logn)
  • 最優時間複雜度 Θ(n)
  • 最差空間複雜度 Θ(n)

合併排序有着較好的漸進運行時間 Θ(nlogn),但其中的 merge 操做不是 in-place 操做。

示例代碼

複製代碼
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = { 4, 1, 5, 2, 6, 3, 7, 9, 8 };
 6 
 7       int[] temp = new int[unsorted.Length];
 8       MergeSort(unsorted, 0, unsorted.Length, temp);
 9       foreach (var key in unsorted)
10       {
11         Console.Write("{0} ", key);
12       }
13 
14       Console.Read();
15     }
16 
17     static void MergeSort(int[] unsorted, int left, int right, int[] temp)
18     {
19       if (left + 1 < right)
20       {
21         // divide
22         int mid = (left + right) / 2;
23 
24         // conquer
25         MergeSort(unsorted, left, mid, temp);
26         MergeSort(unsorted, mid, right, temp);
27 
28         // combine
29         Merge(unsorted, left, mid, right, temp);
30       }
31     }
32 
33     static void Merge(int[] unsorted, int left, int mid, int right, int[] temp)
34     {
35       int leftPosition = left;
36       int rightPosition = mid;
37       int numberOfElements = 0;
38 
39       // merge two slots
40       while (leftPosition < mid && rightPosition < right)
41       {
42         if (unsorted[leftPosition] < unsorted[rightPosition])
43         {
44           temp[numberOfElements++] = unsorted[leftPosition++];
45         }
46         else
47         {
48           temp[numberOfElements++] = unsorted[rightPosition++];
49         }
50       }
51 
52       // add remaining
53       while (leftPosition < mid)
54       {
55         temp[numberOfElements++] = unsorted[leftPosition++];
56       }
57       while (rightPosition < right)
58       {
59         temp[numberOfElements++] = unsorted[rightPosition++];
60       }
61 
62       // copy back
63       for (int n = 0; n < numberOfElements; n++)
64       {
65         unsorted[left + n] = temp[n];
66       }
67     }
68   }
複製代碼

堆排序(Heap Sort)

堆排序(Heap Sort)是指利用堆這種數據結構所設計的一種排序算法。二叉堆數據結構是一種數組對象,它能夠被視爲一棵徹底二叉樹。樹中每一個節點與數組中存放該節點值的那個元素對應。

二叉堆有兩種,最大堆和最小堆。最大堆特性是指除了根之外的每一個節點 i ,有 A(Parent(i)) ≥ A[i] ,即某個節點的值至可能是和其父節點的值同樣大。最小堆特性是指除了根之外的每一個節點 i ,有 A(Parent(i)) ≤ A[i] ,最小堆的最小元素在根部。

在堆排序算法中,咱們使用的是最大堆。最小堆一般在構造有限隊列時使用。

堆能夠被當作一棵樹,節點在堆中的高度定義爲從本節點到葉子的最長簡單降低路徑上邊的數目;定義堆的高度爲樹根的高度。由於具備 n 個元素的堆是基於一棵徹底二叉樹,於是其高度爲 Θ(lg n) 。

堆節點的訪問

一般堆是經過一維數組來實現的。在數組起始爲 0 的情形中,若是 i 爲當前節點的索引,則有

  • 父節點在位置 floor((i-1)/2);
  • 左子節點在位置 (2*i+1);
  • 右子節點在位置 (2*i+2);

堆的操做

在堆的數據結構中,堆中的最大值老是位於根節點。堆中定義如下幾種操做:

  • 最大堆調整(Max-Heapify):將堆的末端子節點做調整,使得子節點永遠小於父節點,保持最大堆性質的關鍵。運行時間爲 O(lg n)。
  • 建立最大堆(Build-Max-Heap):在無序的輸入數組基礎上構造出最大堆。運行時間爲 O(n)。
  • 堆排序(HeapSort):對一個數組進行原地排序,卸載位在第一個數據的根節點,並作最大堆調整的遞歸運算。運行時間爲 O(n*lg n)。
  • 抽取最大值(Extract-Max):至關於執行一次最大堆調整,最大值在根處。運行時間爲 O(lg n)。

算法複雜度

  • 最差時間複雜度 O(n*logn)
  • 平均時間複雜度 Θ(n*logn)
  • 最優時間複雜度 O(n*logn)
  • 最差空間複雜度 O(n),輔助空間 O(1)

示例代碼

複製代碼
 1   class Program
 2   {
 3     static void Main(string[] args)
 4     {
 5       int[] unsorted = { 4, 1, 5, 2, 6, 3, 7, 9, 8 };
 6 
 7       HeapSort(unsorted);
 8 
 9       foreach (var key in unsorted)
10       {
11         Console.Write("{0} ", key);
12       }
13 
14       Console.Read();
15     }
16 
17     static void HeapSort(int[] unsorted)
18     {
19       // build the heap in array so that largest value is at the root
20       BuildMaxHeap(unsorted);
21 
22       // swap root node and the last heap node
23       for (int i = unsorted.Length - 1; i >= 1; i--)
24       {
25         // array[0] is the root and largest value. 
26         // the swap moves it in front of the sorted elements
27         int max = unsorted[0];
28         unsorted[0] = unsorted[i];
29         unsorted[i] = max; // now, the largest one is at the end
30 
31         // the swap ruined the heap property, so restore it
32         // the heap size is reduced by one
33         MaxHeapify(unsorted, 0, i - 1);
34       }
35     }
36 
37     static void BuildMaxHeap(int[] unsorted)
38     {
39       // put elements of array in heap order, in-place
40       // start is assigned the index in array of the last parent node
41       // the last element in 0-based array is at index count-1; 
42       // find the parent of that element
43       for (int i = (unsorted.Length / 2) - 1; i >= 0; i--)
44       {
45         // sift down the node at index start to the proper place 
46         // such that all nodes below the start index are in heap order
47         MaxHeapify(unsorted, i, unsorted.Length - 1);
48       }
49       // after sifting down the root all nodes/elements are in heap order
50     }
51 
52     static void MaxHeapify(int[] unsorted, int root, int bottom)
53     {
54       int rootValue = unsorted[root];
55       int maxChild = root * 2 + 1; // start from left child
56 
57       // while the root has at least one child
58       while (maxChild <= bottom)
59       {
60         // more children
61         if (maxChild < bottom)
62         {
63           // if there is a right child and that child is greater
64           if (unsorted[maxChild] < unsorted[maxChild + 1])
65           {
66             maxChild = maxChild + 1;
67           }
68         }
69 
70         // compare roots and the older children
71         if (rootValue < unsorted[maxChild])
72         {
73           unsorted[root] = unsorted[maxChild];
74           root = maxChild;
75 
76           // repeat to continue sifting down the child now
77           maxChild = root * 2 + 1; // continue from left child
78         }
79         else
80         {
81           maxChild = bottom + 1;
82         }
83       }
84 
85       unsorted[root] = rootValue;
86     }
87   }
複製代碼

內省排序(Introspective Sort)

內省排序(Introsort)是由 David Musser 在 1997 年設計的排序算法。這個排序算法首先從快速排序開始,當遞歸深度超過必定深度(深度爲排序元素數量的對數值)後轉爲堆排序。採用這個方法,內省排序既能在常規數據集上實現快速排序的高性能,又能在最壞狀況下仍保持 O(nlog n) 的時間複雜度。因爲這兩種算法都屬於比較排序算法,因此內省排序也是一個比較排序算法。

在快速排序算法中,一個關鍵操做就是選擇主元(Pivot):數列將被此主元位置分開成兩部分。最簡單的主元選擇算法是使用第一個或者最後一個元素,但這在排列已部分有序的序列上性能不好。Niklaus Wirth 爲此設計了一個快速排序的變體,使用處於中間的元素來防止在某些特定序列上性能退化爲 O(n2) 的情況。該 median-of-3 選擇算法從序列的第1、中間和最後一個元素取得中位數來做爲主元。雖然這個算法在現實世界的數據上性能表現良好,但通過精心設計的序列仍能大幅下降此算法性能。這樣就有攻擊者精心設計序列發送到因特網服務器以進行拒絕服務(DOS:Denial of Service)攻擊的潛在可能性。

Musser 研究指出,在爲 median-of-3 選擇算法精心設計的 100,000 個元素序列上,內省排序算法的運行時間是快速排序的 1/200。在 Musser 的算法中,最終較小範圍內數據的排序由 Robert Sedgewick 提出的小數據排序算法完成。

算法複雜度

  • 最差時間複雜度 O(n*log n)
  • 平均時間複雜度 O(n*log n)
  • 最優時間複雜度 O(n*log n)

微軟 .NET 框架中 Array.Sort 方法的實現使用了內省排序(Introspective Sort)算法。

代碼示例

複製代碼
  1   class Program
  2   {
  3     static void Main(string[] args)
  4     {
  5       int[] unsorted = { 
  6                          4, 1, 5, 2, 6, 3, 7, 9, 8, 10,
  7                          20, 11, 19, 12, 18, 17, 15, 16, 13, 14
  8                        };
  9 
 10       GenericQuickSorter<int>.DepthLimitedQuickSort(
 11         unsorted, 0, unsorted.Length - 1, 
 12         GenericQuickSorter<int>.QuickSortDepthThreshold);
 13 
 14       foreach (var key in unsorted)
 15       {
 16         Console.Write("{0} ", key);
 17       }
 18 
 19       Console.Read();
 20     }
 21   }
 22 
 23   internal class GenericQuickSorter<T>
 24       where T : IComparable<T>
 25   {
 26     internal const int QuickSortDepthThreshold = 32;
 27 
 28     internal static void DepthLimitedQuickSort(T[] keys, int left, int right, int depthLimit)
 29     {
 30       do
 31       {
 32         if (depthLimit == 0)
 33         {
 34           Heapsort(keys, left, right);
 35           return;
 36         }
 37 
 38         int i = left;
 39         int j = right;
 40 
 41         // pre-sort the low, middle (pivot), and high values in place.
 42         // this improves performance in the face of already sorted data, or 
 43         // data that is made up of multiple sorted runs appended together.
 44         int middle = i + ((j - i) >> 1);
 45         SwapIfGreater(keys, i, middle);  // swap the low with the mid point
 46         SwapIfGreater(keys, i, j);       // swap the low with the high
 47         SwapIfGreater(keys, middle, j);  // swap the middle with the high
 48 
 49         T x = keys[middle];
 50         do
 51         {
 52           while (keys[i].CompareTo(x) < 0) i++;
 53           while (x.CompareTo(keys[j]) < 0) j--;
 54           Contract.Assert(i >= left && j <= right,
 55             "(i>=left && j<=right)  Sort failed - Is your IComparer bogus?");
 56 
 57           if (i > j) break;
 58           if (i < j)
 59           {
 60             T key = keys[i];
 61             keys[i] = keys[j];
 62             keys[j] = key;
 63           }
 64           i++;
 65           j--;
 66         } while (i <= j);
 67 
 68         // The next iteration of the while loop is to 
 69         // "recursively" sort the larger half of the array and the
 70         // following calls recrusively sort the smaller half.  
 71         // So we subtrack one from depthLimit here so
 72         // both sorts see the new value.
 73         depthLimit--;
 74 
 75         if (j - left <= right - i)
 76         {
 77           if (left < j) DepthLimitedQuickSort(keys, left, j, depthLimit);
 78           left = i;
 79         }
 80         else
 81         {
 82           if (i < right) DepthLimitedQuickSort(keys, i, right, depthLimit);
 83           right = j;
 84         }
 85       } while (left < right);
 86     }
 87 
 88     private static void SwapIfGreater(T[] keys, int a, int b)
 89     {
 90       if (a != b)
 91       {
 92         if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
 93         {
 94           T key = keys[a];
 95           keys[a] = keys[b];
 96           keys[b] = key;
 97         }
 98       }
 99     }
100 
101     private static void Heapsort(T[] keys, int lo, int hi)
102     {
103       Contract.Requires(keys != null);
104       Contract.Requires(lo >= 0);
105       Contract.Requires(hi > lo);
106       Contract.Requires(hi < keys.Length);
107 
108       int n = hi - lo + 1;
109       for (int i = n / 2; i >= 1; i = i - 1)
110       {
111         DownHeap(keys, i, n, lo);
112       }
113       for (int i = n; i > 1; i = i - 1)
114       {
115         Swap(keys, lo, lo + i - 1);
116         DownHeap(keys, 1, i - 1, lo);
117       }
118     }
119 
120     private static void DownHeap(T[] keys, int i, int n, int lo)
121     {
122       Contract.Requires(keys != null);
123       Contract.Requires(lo >= 0);
124       Contract.Requires(lo < keys.Length);
125 
126       T d = keys[lo + i - 1];
127       int child;
128       while (i <= n / 2)
129       {
130         child = 2 * i;
131         if (child < n
132           && (keys[lo + child - 1] == null
133             || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
134         {
135           child++;
136         }
137         if (keys[lo + child - 1] == null
138           || keys[lo + child - 1].CompareTo(d) < 0)
139           break;
140         keys[lo + i - 1] = keys[lo + child - 1];
141         i = child;
142       }
143       keys[lo + i - 1] = d;
144     }
145 
146     private static void Swap(T[] a, int i, int j)
147     {
148       if (i != j)
149       {
150         T t = a[i];
151         a[i] = a[j];
152         a[j] = t;
153       }
154     }
155   }
複製代碼

.NET Array.Sort 實現

微軟 .NET 框架中 Array.Sort 方法的實現使用了內省排序(Introspective Sort)算法:

這種實現也是不穩定的排序,也就是說,若是兩個元素相等,則並不能保證它們的順序,而相反一個穩定的排序算法則會保持相等元素原來的順序。

若是數組的長度爲 n ,則該實現的平均狀況爲 O(n log n) ,最壞狀況爲 O(n2)。

注:.NET 4.5 中使用這裏描述的算法,而 .NET 4.0 及之前版本使用上面描述的快速排序和堆排序組合的內省排序算法。

複製代碼
  1   class Program
  2   {
  3     static void Main(string[] args)
  4     {
  5       int[] unsorted = { 
  6                          4, 1, 5, 2, 6, 3, 7, 9, 8, 10,
  7                          20, 11, 19, 12, 18, 17, 15, 16, 13, 14
  8                        };
  9 
 10       GenericIntroSorter<int>.IntrospectiveSort(unsorted, 0, unsorted.Length);
 11 
 12       foreach (var key in unsorted)
 13       {
 14         Console.Write("{0} ", key);
 15       }
 16 
 17       Console.Read();
 18     }
 19   }
 20 
 21   internal class GenericIntroSorter<T>
 22       where T : IComparable<T>
 23   {
 24     internal const int IntrosortSwitchToInsertionSortSizeThreshold = 16;
 25 
 26     internal static void IntrospectiveSort(T[] keys, int left, int length)
 27     {
 28       Contract.Requires(keys != null);
 29       Contract.Requires(left >= 0);
 30       Contract.Requires(length >= 0);
 31       Contract.Requires(length <= keys.Length);
 32       Contract.Requires(length + left <= keys.Length);
 33 
 34       if (length < 2)
 35         return;
 36 
 37       IntroSort(keys, left, length + left - 1, 2 * FloorLog2(keys.Length));
 38     }
 39 
 40     private static void IntroSort(T[] keys, int lo, int hi, int depthLimit)
 41     {
 42       Contract.Requires(keys != null);
 43       Contract.Requires(lo >= 0);
 44       Contract.Requires(hi < keys.Length);
 45 
 46       while (hi > lo)
 47       {
 48         int partitionSize = hi - lo + 1;
 49         if (partitionSize <= IntrosortSwitchToInsertionSortSizeThreshold)
 50         {
 51           if (partitionSize == 1)
 52           {
 53             return;
 54           }
 55           if (partitionSize == 2)
 56           {
 57             SwapIfGreaterWithItems(keys, lo, hi);
 58             return;
 59           }
 60           if (partitionSize == 3)
 61           {
 62             SwapIfGreaterWithItems(keys, lo, hi - 1);
 63             SwapIfGreaterWithItems(keys, lo, hi);
 64             SwapIfGreaterWithItems(keys, hi - 1, hi);
 65             return;
 66           }
 67 
 68           InsertionSort(keys, lo, hi);
 69           return;
 70         }
 71 
 72         if (depthLimit == 0)
 73         {
 74           Heapsort(keys, lo, hi);
 75           return;
 76         }
 77         depthLimit--;
 78 
 79         int p = PickPivotAndPartition(keys, lo, hi);
 80 
 81         // Note we've already partitioned around the pivot 
 82         // and do not have to move the pivot again.
 83         IntroSort(keys, p + 1, hi, depthLimit);
 84         hi = p - 1;
 85       }
 86     }
 87 
 88     private static int PickPivotAndPartition(T[] keys, int lo, int hi)
 89     {
 90       Contract.Requires(keys != null);
 91       Contract.Requires(lo >= 0);
 92       Contract.Requires(hi > lo);
 93       Contract.Requires(hi < keys.Length);
 94       Contract.Ensures(Contract.Result<int>() >= lo && Contract.Result<int>() <= hi);
 95 
 96       // Compute median-of-three.  But also partition them.
 97       int middle = lo + ((hi - lo) / 2);
 98 
 99       // Sort lo, mid and hi appropriately, then pick mid as the pivot.
100       SwapIfGreaterWithItems(keys, lo, middle);  // swap the low with the mid point
101       SwapIfGreaterWithItems(keys, lo, hi);      // swap the low with the high
102       SwapIfGreaterWithItems(keys, middle, hi);  // swap the middle with the high
103 
104       T pivot = keys[middle];
105       Swap(keys, middle, hi - 1);
106 
107       // We already partitioned lo and hi and put the pivot in hi - 1.  
108       // And we pre-increment & decrement below.
109       int left = lo, right = hi - 1;
110 
111       while (left < right)
112       {
113         if (pivot == null)
114         {
115           while (left < (hi - 1) && keys[++left] == null) ;
116           while (right > lo && keys[--right] != null) ;
117         }
118         else
119         {
120           while (pivot.CompareTo(keys[++left]) > 0) ;
121           while (pivot.CompareTo(keys[--right]) < 0) ;
122         }
123 
124         if (left >= right)
125           break;
126 
127         Swap(keys, left, right);
128       }
129 
130       // Put pivot in the right location.
131       Swap(keys, left, (hi - 1));
132 
133       return left;
134     }
135 
136     private static void Heapsort(T[] keys, int lo, int hi)
137     {
138       Contract.Requires(keys != null);
139       Contract.Requires(lo >= 0);
140       Contract.Requires(hi > lo);
141       Contract.Requires(hi < keys.Length);
142 
143       int n = hi - lo + 1;
144       for (int i = n / 2; i >= 1; i = i - 1)
145       {
146         DownHeap(keys, i, n, lo);
147       }
148       for (int i = n; i > 1; i = i - 1)
149       {
150         Swap(keys, lo, lo + i - 1);
151         DownHeap(keys, 1, i - 1, lo);
152       }
153     }
154 
155     private static void DownHeap(T[] keys, int i, int n, int lo)
156     {
157       Contract.Requires(keys != null);
158       Contract.Requires(lo >= 0);
159       Contract.Requires(lo < keys.Length);
160 
161       T d = keys[lo + i - 1];
162       int child;
163       while (i <= n / 2)
164       {
165         child = 2 * i;
166         if (child < n
167           && (keys[lo + child - 1] == null
168             || keys[lo + child - 1].CompareTo(keys[lo + child]) < 0))
169         {
170           child++;
171         }
172         if (keys[lo + child - 1] == null
173           || keys[lo + child - 1].CompareTo(d) < 0)
174           break;
175         keys[lo + i - 1] = keys[lo + child - 1];
176         i = child;
177       }
178       keys[lo + i - 1] = d;
179     }
180 
181     private static void InsertionSort(T[] keys, int lo, int hi)
182     {
183       Contract.Requires(keys != null);
184       Contract.Requires(lo >= 0);
185       Contract.Requires(hi >= lo);
186       Contract.Requires(hi <= keys.Length);
187 
188       int i, j;
189       T t;
190       for (i = lo; i < hi; i++)
191       {
192         j = i;
193         t = keys[i + 1];
194         while (j >= lo && (t == null || t.CompareTo(keys[j]) < 0))
195         {
196           keys[j + 1] = keys[j];
197           j--;
198         }
199         keys[j + 1] = t;
200       }
201     }
202 
203     private static void SwapIfGreaterWithItems(T[] keys, int a, int b)
204     {
205       Contract.Requires(keys != null);
206       Contract.Requires(0 <= a && a < keys.Length);
207       Contract.Requires(0 <= b && b < keys.Length);
208 
209       if (a != b)
210       {
211         if (keys[a] != null && keys[a].CompareTo(keys[b]) > 0)
212         {
213           T key = keys[a];
214           keys[a] = keys[b];
215           keys[b] = key;
216         }
217       }
218     }
219 
220     private static void Swap(T[] a, int i, int j)
221     {
222       if (i != j)
223       {
224         T t = a[i];
225         a[i] = a[j];
226         a[j] = t;
227       }
228     }
229 
230     private static int FloorLog2(int n)
231     {
232       int result = 0;
233       while (n >= 1)
234       {
235         result++;
236         n = n / 2;
237       }
238       return result;
239     }
240   }
複製代碼
相關文章
相關標籤/搜索