1. 程式人生 > >面試題:top k演算法O(n)時間複雜度

面試題:top k演算法O(n)時間複雜度

在《資料結構與演算法分析--c語言描述》一書,第7章第7.7.6節中,闡述了一種在平均情況下,時間複雜度為O(N)的快速選擇演算法。如下述文字:

  • 選取S中一個元素作為樞紐元v,將集合S-{v}分割成S1和S2,就像快速排序那樣
    • 如果k <= |S1|,那麼第k個最小元素必然在S1中。在這種情況下,返回QuickSelect(S1, k)。
    • 如果k = 1 + |S1|,那麼樞紐元素就是第k個最小元素,即找到,直接返回它。
    • 否則,這第k個最小元素就在S2中,即S2中的第(k - |S1| - 1)個最小元素,我們遞迴呼叫並返回QuickSelect(S2, k - |S1| - 1)。

此演算法的平均執行時間為O(n)。

示例程式碼如下:

//QuickSelect 將第k小的元素放在 a[k-1]  
void QuickSelect( int a[], int k, int left, int right )
{
    int i, j;
    int pivot;

    if( left + cutoff <= right )
    {
        pivot = median3( a, left, right );
        //取三數中值作為樞紐元,可以很大程度上避免最壞情況
        i = left; j = right - 1;
        for( ; ; )
        {
            while
( a[ ++i ] < pivot ){ } while( a[ --j ] > pivot ){ } if( i < j ) swap( &a[ i ], &a[ j ] ); else break; } //重置樞紐元 swap( &a[ i ], &a[ right - 1 ] ); if( k <= i ) QuickSelect
( a, k, left, i - 1 ); else if( k > i + 1 ) QuickSelect( a, k, i + 1, right ); } else InsertSort( a + left, right - left + 1 ); }

這個快速選擇SELECT演算法,類似快速排序的劃分方法。N個數儲存在陣列S中,再從陣列中選取“中位數的中位數”作為樞紐元X,把陣列劃分為Sa和Sb倆部分,Sa<=X<=Sb,如果要查詢的k個元素小於Sa的元素個數,則返回Sa中較小的k個元素,否則返回Sa中所有元素+Sb中小的k-|Sa|個元素,這種解法在平均情況下能做到O(n)的複雜度。

更進一步,《演算法導論》第9章第9.3節介紹了一個最壞情況下亦為O(n)時間的SELECT演算法,有興趣的讀者可以參看。