面試題:top k演算法O(n)時間複雜度
阿新 • • 發佈:2018-12-29
在《資料結構與演算法分析--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演算法,有興趣的讀者可以參看。