在系統中,我們經常會遇到這樣的需求:將大量(比如幾十萬、甚至上百萬)的物件進行排序,然後只需要取出最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。該實現是執行緒安全的。
////
///被排名的物件的標誌型別
///被排名的物件型別
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
addlist
public
void
add(ilist
<
tobj
>
list)
using
(this
.smartrwlocker.lock(accessmode.write))}}
#endregion
#region
addpublic
void
add(tobjobj)
}#endregion
#region
gettopn
public
tobjgettopn()
}#endregion
#region
private
#region
doadd
private
void
doadd(tobjobj)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
tobjgettopn
<
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(userdataother)
public
override
string
tostring()
#endregion}
private
void
button4_click(
object
sender,eventargse)
list
<
userdata
>
list2
=new
list
<
userdata
>
();for
(inti=
0;i<
500000
;i++
)stopwatchstopwatch
=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);
userdatares
=container.gettopn();
stopwatch.stop();
long
ms2=
stopwatch.elapsedmilliseconds;
}#region
icomparer成員
public
intcompare(userdatax,userdatay)
#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 ...