從n個數中提取最小的m個數的演算法
2007-02-10 23:58:45
分類:
經常在網上看到有人討論這個問題:
如何高效地從n個數中提取最小的m個數?
或者是其他類似的問題,今天我也簡單地分析一下。
具體問題具體分析,既然這個題目只要求我們找出這m個數,沒有要求對其進行排序,所以負擔也就輕了,相應地也能採用更高效的資料結構和演算法。如果不要求空間複雜度,並且m不大,我們可以開闢另外乙個空間(s)儲存這m個數,一般的時候空間複雜度要求都是較低的,所以我們也可以這樣假設。n個數中的前m個數我們可以直接放在空間s中,當取第m + 1個數的時候,我們就要考慮這個數是否要加入到空間s中,如果加入,應該遵循乙個什麼樣的替換規則。我們需要找出的是最小的m個數,所以這m個數中最大的數m就是基準,如果後續的數比m大,那麼就不應該加入空間,如果比m小,就要加入空間。當新數n需要加入空間時,被擠掉的數肯定是先前最大的數m,那麼新數應該放在哪個位置呢?複雜度集中在如何找出最大的數m和如何插入新數n。其實,這兩個問題是相關的,焦點就積聚在搜尋最大資料和插入新資料的操作上。也許大家已經想到了,最大堆不就正適合此種情況嗎?其最大數就是根元素,查詢的時間複雜度為o(1),新資料的插入時間複雜度為o(log(n)),已經為理論上的最優解。
c++的程式原始碼:
#include
<
iostream
>
#include
#include
#include
<
vector
>
using
namespace
std;
int main(
int argc,
char
*argv)
cout
<
<
endl
;for
(int i = 0; i < n; i ++)
if(t >
= val[0]
)continue
;pop_heap
(val.begin(
), val.end())
;val[m - 1]
= t;
push_heap
(val.begin(
), val.end())
;}cout
<
<
"top "
<
< m <
<
":"<
<
endl
;for
(int i = 0; i < m; i ++)
cout
<
< val[i]
<
<
" ";
cout
<
<
endl
;sort_heap
(val2.begin(
), val2.end())
;cout
<
<
"sorted top "
<
< m <
<
":"<
<
endl
;for
(int i = 0; i < m; i ++)
cout
<
< val2[i]
<
<
" ";
cout
<
<
endl
;return 0;
}
**很簡單,如果你足夠細心你會發現這個演算法的實際時間複雜度為:
n * 2 * log ( m )
為什麼多了係數2呢?因為pop_heap和push_heap的時間複雜度都為log(m),且每次空間s的更新操作都需要做這兩步。再次考察這兩個操作,如果你熟悉heap,就會發現pop_heap和push_heap兩步可以合併成一步,請看pop_heap的主要步驟:
將根元素取下來。
將末尾的元素取下來。
從根開始搜尋將第2步取下的元素插入到堆中的適當位置。
因為我們在pop之後馬上就需要再次push,所以兩步可以合併為:
將根元素去下來。
從根開始搜尋將要push的元素插入到堆中的適當位置。
具體**請看客自己實現吧,不要太懶了,懶惰可不是什麼好毛病!
另外,c++的stl也有相關演算法模板:
template
<
class randomaccessiterator>
void
nth_element
(randomaccessiterator first, randomaccessiterator nth,
randomaccessiterator last)
;template
<
class randomaccessiterator,
class strictweakordering>
void
nth_element
(randomaccessiterator first, randomaccessiterator nth,
randomaccessiterator last, strictweakordering comp)
;
用其改寫的上述**簡單了很多:
#
include
#include
<
iostream
>
#include
#include
#include
<
vector
>
using
namespace
std;
int main(
int argc,
char
*argv)
cout
<
<
endl
;nth_element
(val.begin(
), val.begin(
)+ m - 1, val.end())
;cout
<
<
"top "
<
< m <
<
":"<
<
endl
;for
(int i = 0; i < m; i ++)
cout
<
< val[i]
<
<
" ";
cout
<
<
endl
;sort
(val2.begin(
), val2.end())
;cout
<
<
"sorted top "
<
< m <
<
":"<
<
endl
;for
(int i = 0; i < m; i ++)
cout
<
< val2[i]
<
<
" ";
cout
<
<
endl
;return 0;
}
以上**也算是nth_element應用的乙個範例吧,至於它的具體實現,如果感興趣還是自己分析,目前我也沒有詳細看。
從n個數中刪除m個數
補題 題目大意是 有個很大的整數n,刪除其中的m位數字,使得剩下的數字按原來的次序組成的數最大。include include include include include include include define ll long long define inf 0x3f3f3f3f usin...
從m個數中選擇n個數的實現
從m個數中選出n個數來 0 n m 要求n個數之間不能有重複,其和等於乙個定值k。求一段程式,羅列所有的可能。例如備選的數字是 11,18,12,1,2,20,8,10,7,6 和k等於 18 那麼組合的可能有 18 8,10 2,20 12,6 11,7 11,1,6 1,10,7 12,2,8 ...
從N個數中等概率列印M個數
題目 給定乙個長度為n且 沒有重複元素 的陣列array和乙個整數m,實現函式等概論隨機列印array中的m個數。要求 1.相同的數不要重複列印 2.時間複雜度為o m 額外空間複雜度為o 1 3.可以改變array陣列 解題思路 解法的關鍵點是利用要求3改變陣列array。列印過程如下 1.在 0...