1. 程式人生 > >期望為線性時間的選擇演算法

期望為線性時間的選擇演算法

《演算法導論》第9章Randomized_Select演算法

Randomized_Select的原理與思想

從一個數組當中尋找第i小的元素,最簡單最暴力的方法就是將整個陣列按照升序進行排序操作,那麼第i個元素就是第i小的元素。
如果是以這種方式,那麼時間複雜度等同於排序時所使用的排序演算法,如果是快速排序,那麼此時時間複雜度為O(nlgn)。

那麼,有沒有一個演算法相對於整個陣列排序再查詢來的更快呢?比如線性時間O(n)?
Randomized_Select就是這樣一個基於隨機函式和快排引申出來的查詢函式。在期望線性時間內,可以找到任一順序統計量,特別是中位數。
  • 第一步 通過隨機函式產生一個下標,這個下標大於等於左邊界,小於等於右邊界
int Randomized_Partition(vector<int>&p, int Left, int Right)
      
      
        {
      
      
        	if (Left == Right)
      
      
        	{
      
      
        		return Left;
      
      
        	}
      
      
        	srand((unsigned int)time(0));
      
      
        	int key = (rand() % (Right - Left + 1)) + Left;
      
      
        	for (int i = Left - 1, j = Left; j <= Right; j++)
      
      
        	{
      
      
        		if (p[j] < p[key])
      
      
        		{
      
      
        			i++;
      
      
        			swap(p[i], p[j]);
      
      
        			if (i == key)
      
      
        			{
      
      
        				key = j;
      
      
        			}
      
      
        		}
      
      
        		if (j == Right)
      
      
        		{
      
      
        			swap(p[key], p[i + 1]);
      
      
        			key = i + 1;
      
      
        		}
      
      
        	}
      
      
        	return key;
      
      
        }
  • 隨機產生一個下標值,記為key,以key代表的值將整個陣列劃分為兩部分,這裡與快排一致。
  • swap操作可能會改變key所代表的元素值,故在對key進行swap操作時需記錄新的key值
  • 劃分完成之後返回key
int Randomized_Select(vector<int>&p, int Left, int Right, int i)
      
      
        {
      
      
        	if (Left == Right)
      
      
        	{
      
      
        		return p[Left];
      
      
        	}
      
      
        	int q = Randomized_Partition(p, Left, Right);
      
      
        	int k = q - Left + 1;
      
      
        	if (k==i)
      
      
        	{
      
      
        		return p[q];
      
      
        	}
      
      
        	else if(i<k)
      
      
        	{
      
      
        		return Randomized_Select(p, Left, q - 1, i);
      
      
        	}
      
      
        	else
      
      
        	{
      
      
        		return Randomized_Select(p, q + 1, Right, i - k);
      
      
        	}
      
      
        

      
      
        }
  • Randomized_Select接受返回的key值,計算下標是否等於i
  • 因為劃分保證了當前陣列中,key前面的元素都比key所代表的元素小,所以key所代表的元素是陣列中第key小的值
  • 當key等於i時,說明所需值就是key,返回即可
  • 如果i小於key,說明所需值在key的左部,需要進一步劃分
  • 如果i大於key,說明所需值在key的右部,等同於在key的右部劃分中需要第i-key小的元素

總結

利用隨機數進行劃分,從數學角度上將最壞執行時間給平均掉了,等概率劃分的情況下使得最壞執行時間也不會太差。 書中還介紹了一種最壞情況為線性時間的選擇演算法,下次再寫。