在一个由 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 }

浙公网安备 33010602011771号