從n個數中提取最小的m個數的演算法

2021-06-26 17:48:54 字數 3772 閱讀 4973

從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...