TopN演算法與排行榜

2021-05-06 23:12:17 字數 4675 閱讀 1175

在系統中,我們經常會遇到這樣的需求:將大量(比如幾十萬、甚至上百萬)的物件進行排序,然後只需要取出最top的前n名作為排行榜的資料,這即是乙個topn演算法。常見的解決方案有三種:

(1)直接使用list的sort方法進行處理。

(2)使用排序二叉樹進行排序,然後取出前n名。

(3)使用最大堆排序,然後取出前n名。

第一種方案的效能是最差的,後兩種方案效能會好一些,但是還是不能滿足我們的需求。最主要的原因在於使用二叉樹和最大堆排序時,都是對所有的物件進行排序,而不是將代價花費在我們需要的少數的topn上。為此,我自己實現了topnorderedcontainer來解決這個問題。

思路是這樣的,使用乙個長度為n的陣列,來存放最top的n個物件,越top的物件其在陣列中的index就越小。這樣,每次加入乙個物件時,就與index最大的那個物件比較,如果比其更top,則交換兩個物件的位置。如果被交換的物件是陣列中的最後乙個物件(index最大),則該物件會被拋棄。如此,可以保證容器中始終保持的都是最top的n個物件。

接下來我們看具體的實現。

如果乙個物件要參與topn排行榜,則其必須實現iordered介面,表明其可以被top排序。

//////

iordered 參與排行榜排序的物件必須實現的介面。

///

///參與排行榜排序的物件的型別

public

inte***ce

iordered

<

torderedobj

>

之所以使用泛型引數torderedobj,是為了避免派生類在實現istopthan方法時,需要將引數other進行向下轉換。

接下來是topnorderedcontainer實現的原始碼:

//////

topnorderedcontainer 用於始終保持排行榜前n名的object。該實現是執行緒安全的。

///zhuweisky 2009.05.23

///

///被排名的物件的標誌型別

///被排名的物件型別

public

class

topnorderedcontainer

<

tobj

>

where

tobj : iordered

<

tobj

>

set} 

#endregion

#region

ctor

public

topnorderedcontainer() 

public

topnorderedcontainer(

int_topnumber)

#endregion

#region

initialize

public

void

initialize()

this

.orderedarray 

=new

tobj[

this

.topnumber];

} #endregion

#region

add list

public

void

add(ilist

<

tobj

>

list)

using

(this

.smartrwlocker.lock(accessmode.write))}} 

#endregion

#region

addpublic

void

add(tobj obj)

} #endregion

#region

gettopn

public

tobj gettopn()

} #endregion

#region

private

#region

doadd

private

void

doadd(tobj obj)if(

this

.validobjcount 

<

this

.topnumber)if(

this

.orderedarray[

this

.topnumber -1

].istopthan(obj))

this

.orderedarray[

this

.topnumber -1

] =obj;

this

.adjust(

this

.topnumber -1

);}#endregion

#region

adjust

//////

adjust 調整posindex處的物件到合適的位置。

///與相鄰前乙個物件比較,如果當前物件更加top,則與前乙個物件交換位置。

///

private

void

adjust(

intposindex)

else}}

#endregion

#endregion}

原始碼面前毫無秘密。

但是有幾點我還是需要說明一下:

(2)為何不將topn排序直接實現為乙個靜態方法,如:

public

static

tobj gettopn

<

tobj

>

(ilist

<

tobj

>

list) 

where

tobj : iordered

<

tobj

>

如果要是這樣實現,那我們就沒有辦法繼續動態的add新的tobj物件進來,如果要達到這樣的目的,就只有構造新的list,再次呼叫static gettopn方法,如此會重複做一些工作。

最後,我們來測試一下topnorderedcontainer與list.sort方法的效能比較,測試的物件數目為500000個,取出top20。測試**如下:  

public

class

userdata : iordered

<

userdata

>

set} 

#endregion

#region

score

private

intscore;

public

intscore

set} 

#endregion

public

userdata(

string

_userid, 

int_score)

#region

iordered成員       

public

bool

istopthan(userdata other)

public

override

string

tostring()

#endregion}

private

void

button4_click(

object

sender, eventargs e)

list

<

userdata

>

list2 

=new

list

<

userdata

>

();for

(inti =

0; i 

<

500000

; i++

)stopwatch stopwatch 

=new

stopwatch();

stopwatch.start();

list.sort(

this

);stopwatch.stop();

long

ms1 

=stopwatch.elapsedmilliseconds;

stopwatch.reset();

stopwatch.start();

topnorderedcontainer

<

userdata

>

container 

=new

topnorderedcontainer

<

userdata

>(20

);container.initialize();

container.add(list2);

userdata res 

=container.gettopn();

stopwatch.stop();

long

ms2 

=stopwatch.elapsedmilliseconds;

}       

#region

icomparer成員

public

intcompare(userdata x, userdata y)

#endregion

測試的結果顯示,使用list.sort方法需要1287ms,而topnorderedcontainer只花了78ms。

TopN演算法與排行榜

在系統中,我們經常會遇到這樣的需求 將大量 比如幾十萬 甚至上百萬 的物件進行排序,然後只需要取出最top的前n名作為排行榜的資料,這即是乙個topn演算法。常見的解決方案有三種 1 直接使用list的sort方法進行處理。2 使用排序二叉樹進行排序,然後取出前n名。3 使用最大堆排序,然後取出前n...

排行榜演算法

快速排序,相對位置不變 varquicksort function arr varpivotindex math.floor arr.length 2 取基準點 varpivot arr.splice pivotindex,1 0 取基準點的值,splice index,1 函式可以返回陣列中被刪除...

mysql製作排行榜 mysql實現排行榜

博主新人一枚,大家可以提出自己的寶貴意見。下來我們進入正題。大家首先要了解介面的場景,再就是排行榜的規則,我們這裡說的中國式排行榜。排行榜總結了一下分為3種 中國式排行 非中國式排行1 非中國式排行2 1 1 1 2 2 2 2 2 3 3 4 4 3 5 5 4 5 6 5 7 7 select ...