首页 > 代码库 > 线性时间选择算法

线性时间选择算法

在一个由 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           else63           {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           else79           {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 发表自博客园,任何未经作者同意的爬虫或人为转载均为耍流氓。