[转]第(前)k大数问题
在網(wǎng)上搜了搜,發(fā)現(xiàn)這種問(wèn)題有很多種解法,并且衍生出來(lái)很多新的問(wèn)題。貼出來(lái)給大家看看。
轉(zhuǎn)自http://summerbell.javaeye.com/blog/510394
---------------------------------------------------------------------------------------------------------
所謂“第(前)k大數(shù)問(wèn)題”指的是在長(zhǎng)度為n(n>=k)的亂序數(shù)組中S找出從大到小順序的第(前)k個(gè)數(shù)的問(wèn)題。
?
解法1:我們可以對(duì)這個(gè)亂序數(shù)組按照從大到小先行排序,然后取出前k大,總的時(shí)間復(fù)雜度為O(n*logn+k)。
?
解法2:利用選擇排序或交互排序,K次選擇后即可得到第k大的數(shù)。總的時(shí)間復(fù)雜度為O(n*k)
?
解法3:利用快速排序的思想,從數(shù)組S中隨機(jī)找出一個(gè)元素X,把數(shù)組分為兩部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。這時(shí)有兩種情況:
1.Sa中元素的個(gè)數(shù)小于k,則Sb中的第k-|Sa|個(gè)元素即為第k大數(shù);
2.Sa中元素的個(gè)數(shù)大于等于k,則返回Sa中的第k大數(shù)。時(shí)間復(fù)雜度近似為O(n)
///
回憶一下快速排序,快排中的每一步,都是將待排數(shù)據(jù)分做兩組,其中一組的數(shù)據(jù)的任何一個(gè)數(shù)都比另一組中的任何一個(gè)大,然后再對(duì)兩組分別做類(lèi)似的操作,然后繼續(xù)下去……
在本問(wèn)題中,假設(shè)N個(gè)數(shù)存儲(chǔ)在數(shù)組S中,我們從數(shù)組S中隨機(jī)找出一個(gè)元素X,把數(shù)組分為兩部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。
這時(shí),有兩種可能性:
1.???Sa中元素的個(gè)數(shù)小于K,Sa中所有的數(shù)和Sb中最大的K-|Sa|個(gè)元素(|Sa|指Sa中元素的個(gè)數(shù))就是數(shù)組S中最大的K個(gè)數(shù)。
2.???Sa中元素的個(gè)數(shù)大于或等于K,則需要返回Sa中最大的K個(gè)元素。
這樣遞歸下去,不斷把問(wèn)題分解成更小的問(wèn)題,平均時(shí)間復(fù)雜度O(N * log2K)。
?
解法4:二分[Smin,Smax]查找結(jié)果X,統(tǒng)計(jì)X在數(shù)組中出現(xiàn),且整個(gè)數(shù)組中比X大的數(shù)目為k-1的數(shù)即為第k大數(shù)。時(shí)間復(fù)雜度平均情況為O(n*logn)
?
解法5:用O(4*n)的方法對(duì)原數(shù)組建最大堆,然后pop出k次即可。時(shí)間復(fù)雜度為O(4*n+k*logn)
?
解法6:維護(hù)一個(gè)k大小的最小堆,對(duì)于數(shù)組中的每一個(gè)元素判斷與堆頂?shù)拇笮?#xff0c;若堆頂較大,則不管,否則,彈出堆頂,將當(dāng)前值插入到堆中。時(shí)間復(fù)雜度O(n*logk)
?
解法7:利用hash保存數(shù)組中元素Si出現(xiàn)的次數(shù),利用計(jì)數(shù)排序的思想,線性從大到小掃描過(guò)程中,前面有k-1個(gè)數(shù)則為第k大數(shù),平均情況下時(shí)間復(fù)雜度O(n)
//
上面類(lèi)快速排序的方法平均時(shí)間復(fù)雜度是線性的。能否有確定的線性算法呢?是否可以通過(guò)改進(jìn)計(jì)數(shù)排序、基數(shù)排序等來(lái)得到一個(gè)更高效的算法呢?答案是肯定的。但算法的適用范圍會(huì)受到一定的限制。
如果所有N個(gè)數(shù)都是正整數(shù),且它們的取值范圍不太大,可以考慮申請(qǐng)空間,記錄每個(gè)整數(shù)出現(xiàn)的次數(shù),然后再?gòu)拇蟮叫∪∽畲蟮腒個(gè)。比如,所有整數(shù)都在(0, MAXN)區(qū)間中的話,利用一個(gè)數(shù)組count[MAXN]來(lái)記錄每個(gè)整數(shù)出現(xiàn)的個(gè)數(shù)(count[i]表示整數(shù)i在所有整數(shù)中出現(xiàn)的個(gè)數(shù))。我們只需要掃描一遍就可以得到count數(shù)組。然后,尋找第K大的元素。
?
?
附注:
1.STL中可以用nth_element求得類(lèi)似的第n大的數(shù)(由謂詞決定),使用的是解法3中的思想,還可以用partial_sort對(duì)區(qū)間進(jìn)行部分排序,得到類(lèi)似前k大的數(shù)(由謂詞決定),它采用的是解法5的思想。
2.求中位數(shù)實(shí)際上是第k大數(shù)的特例。
?
《編程之美》2.5節(jié)課后習(xí)題:
1.如果需要找出N個(gè)數(shù)中最大的K個(gè)不同的浮點(diǎn)數(shù)呢?比如,含有10個(gè)浮點(diǎn)數(shù)的數(shù)組(1.5,1.5,2.5,3.5,3.5,5,0,-1.5,3.5)中最大的3個(gè)不同的浮點(diǎn)數(shù)是(5,3.5,2.5)。
解答:上面的解法均適用,需要注意的是浮點(diǎn)數(shù)比較時(shí)和整數(shù)不同,另外求hashkey的方法也會(huì)略有不同。
2.如果是找第k到第m(0<k<=m<=n)大的數(shù)呢?
解答:如果把問(wèn)題看做m-k+1個(gè)第k大問(wèn)題,則前面解法均適用。但是對(duì)于類(lèi)似前k大這樣的問(wèn)題,最好使用解法5或者解法7,總體復(fù)雜度較低。
3.在搜索引擎中,網(wǎng)絡(luò)上的每個(gè)網(wǎng)頁(yè)都有“權(quán)威性”權(quán)重,如pagerank。如果我們需要尋找權(quán)重最大的K個(gè)網(wǎng)頁(yè),而網(wǎng)頁(yè)的權(quán)重會(huì)不斷地更新,那么算法要如何變動(dòng)以達(dá)到快速更新(incremental update)并及時(shí)返回權(quán)重最大的K個(gè)網(wǎng)頁(yè)?
提示:堆排序?當(dāng)每一個(gè)網(wǎng)頁(yè)權(quán)重更新的時(shí)候,更新堆。還有更好的方法嗎?
解答:要達(dá)到快速的更新,我們可以解法5,使用映射二分堆,可以使更新的操作達(dá)到O(logn)
4.在實(shí)際應(yīng)用中,還有一個(gè)“精確度”的問(wèn)題。我們可能并不需要返回嚴(yán)格意義上的最大的K個(gè)元素,在邊界位置允許出現(xiàn)一些誤差。當(dāng)用戶(hù)輸入一個(gè)query的時(shí)候,對(duì)于每一個(gè)文檔d來(lái)說(shuō),它跟這個(gè)query之間都有一個(gè)相關(guān)性衡量權(quán)重f(query,d)。搜索引擎需要返回給用戶(hù)的就是相關(guān)性權(quán)重最大的K個(gè)網(wǎng)頁(yè)。如果每頁(yè)10個(gè)網(wǎng)頁(yè),用戶(hù)不會(huì)關(guān)心第1000頁(yè)開(kāi)外搜索結(jié)果的“精確度”,稍有誤差是可以接受的。比如我們可以返回相關(guān)性第10001大的網(wǎng)頁(yè),而不是第9999大的。在這種情況下,算法該如何改進(jìn)才能更快更有效率呢?網(wǎng)頁(yè)的數(shù)目可能大到一臺(tái)機(jī)器無(wú)法容納得下,這時(shí)怎么辦呢?
提示:歸并排序?如果每臺(tái)機(jī)器都返回最相關(guān)的K個(gè)文檔,那么所有機(jī)器上最相關(guān)K個(gè)文檔的并集肯定包含全集中最相關(guān)的K個(gè)文檔。由于邊界情況并不需要非常精確,如果每臺(tái)機(jī)器返回最好的K’個(gè)文檔,那么K’應(yīng)該如何取值,以達(dá)到我們返回最相關(guān)的90%*K個(gè)文檔是完全精確的,或者最終返回的最相關(guān)的K個(gè)文檔精確度超過(guò)90%(最相關(guān)的K個(gè)文檔中90%以上在全集中相關(guān)性的確排在前K),或者最終返回的最相關(guān)的K個(gè)文檔最差的相關(guān)性排序沒(méi)有超出110%*K。
解答:正如提示中所說(shuō),可以讓每臺(tái)機(jī)器返回最相關(guān)的K’個(gè)文檔,然后利用歸并排序的思想,得到所有文檔中最相關(guān)的K個(gè)。最好的情況是這K個(gè)文檔在所有機(jī)器中平均分布,這時(shí)每臺(tái)機(jī)器只要K’=K/n(n為所有機(jī)器總數(shù));最壞情況,所有最相關(guān)的K個(gè)文檔只出現(xiàn)在其中的某一臺(tái)機(jī)器上,這時(shí)K’需近似等于K了。我覺(jué)得比較好的做法可以在每臺(tái)機(jī)器上維護(hù)一個(gè)堆,然后對(duì)堆頂元素實(shí)行歸并排序。
5.如第4點(diǎn)所說(shuō),對(duì)于每個(gè)文檔d,相對(duì)于不同的關(guān)鍵字q1,q2,…,qm,分別有相關(guān)性權(quán)重f(d,q1),f(d,q2),…,f(d,qm)。如果用戶(hù)輸入關(guān)鍵字qi之后,我們已經(jīng)獲得了最相關(guān)的K個(gè)文檔,而已知關(guān)鍵字qj跟關(guān)鍵字qi相似,文檔跟這兩個(gè)關(guān)鍵字的權(quán)重大小比較靠近,那么關(guān)鍵字qi的最相關(guān)的K個(gè)文檔,對(duì)尋找qj最相關(guān)的K個(gè)文檔有沒(méi)有幫助呢?
解答:肯定是有幫助的。在搜索關(guān)鍵字qj最相關(guān)的K個(gè)文檔時(shí),可以在qj的“近義詞”相關(guān)文檔中搜索部分,然后在全局的所有文檔中在搜索部分。
閱讀全文類(lèi)別:算法學(xué)習(xí)?查看評(píng)論
轉(zhuǎn)載于:https://www.cnblogs.com/iammatthew/archive/2010/07/22/1803865.html
總結(jié)
以上是生活随笔為你收集整理的[转]第(前)k大数问题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 公用表表达式(CTE)WITH:树型查询
- 下一篇: [D3D] - 用PerfHUD来调试商