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

来源:互联网 发布:销售软件使用心得 编辑:程序博客网 时间:2024/04/27 16:00

    由于我们能够以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 )
{
    
forint 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 );
}
原创粉丝点击