在一個由 n 個元素組成的集合中,第 i 個順序統計量(order statistic)是該集合中第 i 小的元素。也就是說,最小值是第 1 個順序統計量(i = 1),最大值是第 n 個順序統計量(i = n)。
中位數(median)是它所在集合的中點元素。當 n 為奇數時,中位數是唯一的,出現在 i = (n + 1)/2 處。當 n 為偶數時,存在兩個中位數,下中位數 i = n/2 和上中位數 i = n/2 + 1 處。因此,不考慮 n 的奇偶性,中位數總是出現在 i = (n+1)/2 的中位數處。本文中所用的中位數總是指下中位數。
選擇最大值和最小值
對於確定最大值和最小值的問題,n-1 次比較是最優的。
對於同時獲取最大值和最小值,至多需要 3(n/2) 次比較就足以同時找到。如果 n 是奇數,那么總共需要 3(n/2) 次比較。如果 n 是偶數,則可先做一次初始比較,接着做 3((n - 2)/2) 次比較。
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 int[] unsorted = 6 { 7 4, 1, 5, 2, 6, 3, 7, 9, 8, 0 8 }; 9 10 Console.WriteLine("Min: {0}", GetMinimum(unsorted)); 11 Console.WriteLine("Max: {0}", GetMaximum(unsorted)); 12 13 int min, max; 14 GetBothMinMax(unsorted, out min, out max); 15 Console.WriteLine("Min: {0}, Max: {1}", min, max); 16 17 Console.Read(); 18 } 19 20 static int GetMinimum(int[] a) 21 { 22 int min = a[0]; 23 24 // n-1 次比較 25 for (int i = 1; i < a.Length; i++) 26 { 27 if (a[i] < min) 28 min = a[i]; 29 } 30 31 return min; 32 } 33 34 static int GetMaximum(int[] a) 35 { 36 int max = a[0]; 37 38 // n-1 次比較 39 for (int i = 1; i < a.Length; i++) 40 { 41 if (a[i] > max) 42 max = a[i]; 43 } 44 45 return max; 46 } 47 48 static void GetBothMinMax(int[] a, out int min, out int max) 49 { 50 min = a[0]; 51 max = a[0]; 52 53 if (a.Length % 2 > 0) // n 為奇數 54 { 55 for (int i = 1; i < a.Length; i = i + 2) 56 { 57 if (a[i] < a[i + 1]) 58 { 59 if (a[i] < min) min = a[i]; 60 if (a[i + 1] > max) max = a[i + 1]; 61 } 62 else 63 { 64 if (a[i + 1] < min) min = a[i + 1]; 65 if (a[i] > max) max = a[i]; 66 } 67 } 68 } 69 else // n 為偶數 70 { 71 for (int i = 1; i < a.Length - 1; i = i + 2) 72 { 73 if (a[i] < a[i + 1]) 74 { 75 if (a[i] < min) min = a[i]; 76 if (a[i + 1] > max) max = a[i + 1]; 77 } 78 else 79 { 80 if (a[i + 1] < min) min = a[i + 1]; 81 if (a[i] > max) max = a[i]; 82 } 83 } 84 85 if (a[a.Length - 1] < min) min = a[a.Length - 1]; 86 if (a[a.Length - 1] > max) max = a[a.Length - 1]; 87 } 88 } 89 }
選擇中位數或任意位置值
RANDOMIZED-SELECT 算法采用快速排序算法的思想。區別是,快速排序會遞歸地處理划分的兩邊,而 RANDOMIZED-SELECT 則只處理一邊。所以快速排序的期望運行時間是 Θ(n lg n),而 RANDOMIZED-SELECT 的期望運行時間為 Θ(n)。
RANDOMIZED-SELECT 的最壞運行時間為 Θ(n2),即使是要選擇最小元素也是如此。因為它是隨機化的,該算法的平均情況性能較好。
1 public class QuickFindAlgorithm 2 { 3 public static void TestRandomizedQuickFind() 4 { 5 int[] unsorted = 6 { 7 4, 1, 5, 2, 6, 3, 7, 9, 8, 0 8 }; 9 10 Console.WriteLine("Find Value : {0}", 11 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 1)); 12 Console.WriteLine("Find Value : {0}", 13 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 2)); 14 Console.WriteLine("Find Value : {0}", 15 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 3)); 16 Console.WriteLine("Find Value : {0}", 17 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 4)); 18 Console.WriteLine("Find Value : {0}", 19 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 5)); 20 Console.WriteLine("Find Value : {0}", 21 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 6)); 22 Console.WriteLine("Find Value : {0}", 23 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 7)); 24 Console.WriteLine("Find Value : {0}", 25 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 8)); 26 Console.WriteLine("Find Value : {0}", 27 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 9)); 28 Console.WriteLine("Find Value : {0}", 29 RandomizedQuickFind(unsorted, 0, unsorted.Length - 1, 10)); 30 31 int median = RandomizedQuickFind(unsorted, 32 0, unsorted.Length - 1, (unsorted.Length + 1) / 2); 33 Console.WriteLine("Find Median : {0}", median); 34 35 Console.Read(); 36 } 37 38 static int RandomizedQuickFind(int[] a, int p, int r, int i) 39 { 40 if (p == r) 41 return a[p]; 42 43 int q = RandomizedPartition(a, p, r); 44 int k = q - p + 1; 45 46 if (i == k) // the pivot value is the answer 47 { 48 return a[q]; 49 } 50 else if (i < k) // i is in left side 51 { 52 return RandomizedQuickFind(a, p, q - 1, i); 53 } 54 else // i is in right side 55 { 56 return RandomizedQuickFind(a, q + 1, r, i - k); 57 } 58 } 59 60 static void RandomizedQuickSort(int[] unsorted, int left, int right) 61 { 62 if (!(left < right)) return; 63 64 int pivotIndex = RandomizedPartition(unsorted, left, right); 65 66 RandomizedQuickSort(unsorted, left, pivotIndex - 1); 67 RandomizedQuickSort(unsorted, pivotIndex + 1, right); 68 } 69 70 static int RandomizedPartition(int[] unsorted, int left, int right) 71 { 72 int i = random.Next(left, right); 73 Swap(unsorted, i, right); 74 return Partition(unsorted, left, right); 75 } 76 77 static int Partition(int[] unsorted, int left, int right) 78 { 79 int pivotIndex = right; 80 81 // 哨兵 82 int sentinel = unsorted[right]; 83 84 // 子數組長度為 right - left + 1 85 int i = left - 1; 86 for (int j = left; j <= right - 1; j++) 87 { 88 if (unsorted[j] <= sentinel) 89 { 90 i++; 91 Swap(unsorted, i, j); 92 } 93 } 94 95 Swap(unsorted, i + 1, pivotIndex); 96 97 return i + 1; 98 } 99 100 static void Swap(int[] unsorted, int i, int j) 101 { 102 int temp = unsorted[i]; 103 unsorted[i] = unsorted[j]; 104 unsorted[j] = temp; 105 } 106 107 static Random random = new Random(new Guid().GetHashCode()); 108 }
本篇文章《線性時間選擇算法》由 Dennis Gao 發表自博客園,任何未經作者同意的爬蟲或人為轉載均為耍流氓。