您的位置:首页 > 其它

选择问题的线性期望时间算法

2008-03-07 22:30 435 查看
由于我们能够以O(NlogN)时间给数组排序,因此可以期望为选择问题得到一个更好的时间界。我们这里介绍的查找集合S中第k个最小元的算法几乎与快速排序相同。事实上,其前三步是相同的。我们把这种算法叫做快速选择(quickselect)。令|Si|为Si中元素的个数,快速选择的步骤如下:
1)如果|S|=1,那么k=1并将S中的元素作为答案返回。如果正在使用小数组的截止方法且|S|CUTOFF,则将S排序并返回第k个最小元。
2)选取一个枢纽元v∈S。
3)将集合S-{v}分割成S1和S2,就像快速排序中所做的那样。
4)如果k|S1|,那么第K个最小元必然在S1中。在这种情况下,返回quickselect(S1,k).如果k=1+|S1|,那么枢纽元就是第k个最小元,将它最为答案返回。否则,第k个最小元就在S2中,他是S2中的第(k-|S1|-1)个最小元。我们进行一次递归调用并返回quickselect(S2,k-|S1|-1).
与快速排序对比,快速选择只进行了一次递归调用而不是两次。快速选择的最坏情形和快速排序的相同,也就是O(N2)。直观看来,这是因为快速排序的最坏情形发生在S1和S2有一个是空的时候;于是,快速选择也就不是真的节省一次递归调用。不过平均运行时间是O(N)。具体分析类似快速排序的分析。
快速排序的实现甚至比抽象描述还要简单,其程序如下。当算法终止时,第k个最小元就在位置k-1上(因为数组开始于下标0)。这破坏了原来的排序;如果不希望这样,那么需要做一份拷贝。




/**//**


* Simple insertion sort.


*/


template <typename Comparable>


void insertionSort( vector<Comparable> & a )




...{


for( int p = 1; p < a.size( ); p++ )




...{


Comparable tmp = a[ p ];




int j;


for( j = p; j > 0 && tmp < a[ j - 1 ]; j-- )


a[ j ] = a[ j - 1 ];


a[ j ] = tmp;


}


}






/**//**


* Return median of left, center, and right.


* Order these and hide the pivot.


*/


template <typename Comparable>


const Comparable & median3( vector<Comparable> & a, int left, int right )




...{


int center = ( left + right ) / 2;


if( a[ center ] < a[ left ] )


swap( a[ left ], a[ center ] );


if( a[ right ] < a[ left ] )


swap( a[ left ], a[ right ] );


if( a[ right ] < a[ center ] )


swap( a[ center ], a[ right ] );




// Place pivot at position right - 1


swap( a[ center ], a[ right - 1 ] );


return a[ right - 1 ];


}








/**//**


* Internal selection method that makes recursive calls.


* Uses median-of-three partitioning and a cutoff of 10.


* Places the kth smallest item in a[k-1].


* a is an array of Comparable items.


* left is the left-most index of the subarray.


* right is the right-most index of the subarray.


* k is the desired rank (1 is minimum) in the entire array.


*/


template <typename Comparable>


void quickSelect( vector<Comparable> & a, int left, int right, int k )




...{


if( left + 10 <= right )




...{


Comparable pivot = median3( a, left, right );




// Begin partitioning


int i = left, j = right - 1;


for( ; ; )




...{




while( a[ ++i ] < pivot ) ...{ }




while( pivot < a[ --j ] ) ...{ }


if( i < j )


swap( a[ i ], a[ j ] );


else


break;


}




swap( a[ i ], a[ right - 1 ] ); // Restore pivot




// Recurse; only this part changes


if( k <= i )


quickSelect( a, left, i - 1, k );


else if( k > i + 1 )


quickSelect( a, i + 1, right, k );


}


else // Do an insertion sort on the subarray


insertionSort( a, left, right );


}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: