自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

海量無序數(shù)據(jù)尋找第 K 大的數(shù)

開發(fā) 前端
本文簡(jiǎn)單介紹了無序數(shù)組求 Top K 問題和無序數(shù)組求第 K 大數(shù)字兩類非常相似的問題,并且提供了常見的三種解決方案。

 

本文轉(zhuǎn)載自微信公眾號(hào)「Kirito的技術(shù)分享」,作者kiritomoe。轉(zhuǎn)載本文請(qǐng)聯(lián)系Kirito的技術(shù)分享公眾號(hào)。

簡(jiǎn)單抽象一下問題,便是今天的主題:在一個(gè)百萬級(jí)無序的 long 數(shù)組中,尋找第 K 大的數(shù)。要求當(dāng)然是越快找到越好。

top K 問題

題面一描述出來,很多人都會(huì)聯(lián)想到 top K 問題,這道題無論是算法領(lǐng)域還是工程領(lǐng)域,都討論的極其廣泛,并且在實(shí)際項(xiàng)目中也很容易會(huì)遇到類似的問題,我也正好趁著這個(gè)機(jī)會(huì)總結(jié)成一篇文章。

常見的 top K 問題,及其變種:

  1. 有 10000000 個(gè)記錄,這些查詢串的重復(fù)度比較高,如果除去重復(fù)后,不超過 3000000 個(gè)。一個(gè)查詢串的重復(fù)度越高,說明查詢它的用戶越多,也就是越熱門。請(qǐng)統(tǒng)計(jì)最熱門的 10 個(gè)查詢串,要求使用的內(nèi)存不能超過 1GB。
  2. 有 10 個(gè)文件,每個(gè)文件 1GB,每個(gè)文件的每一行存放的都是用戶的 query,每個(gè)文件的 query 都可能重復(fù)。按照 query 的頻度排序。
  3. 有一個(gè) 1GB 大小的文件,里面的每一行是一個(gè)詞,詞的大小不超過 16 個(gè)字節(jié),內(nèi)存限制大小是 1MB。返回頻數(shù)最高的 100 個(gè)詞。
  4. 提取某日訪問網(wǎng)站次數(shù)最多的那個(gè) IP。
  5. 10 億個(gè)整數(shù)找出重復(fù)次數(shù)最多的 100 個(gè)整數(shù)。
  6. 搜索的輸入信息是一個(gè)字符串,統(tǒng)計(jì) 300 萬條輸入信息中最熱門的前 10 條,每次輸入的一個(gè)字符串為不超過 255B,內(nèi)存使用只有 1GB。
  7. 有 1000 萬個(gè)身份證號(hào)以及他們對(duì)應(yīng)的數(shù)據(jù),身份證號(hào)可能重復(fù),找出出現(xiàn)次數(shù)最多的身份證號(hào)。

傳統(tǒng) top K 問題的描述:

在海量數(shù)據(jù)中找出最大的前 K 個(gè)數(shù)

注意我這次提出的問題和傳統(tǒng) top K 有一點(diǎn)區(qū)別,傳統(tǒng)的 top K 問題要求的一般是”前 K 大的數(shù)“,而我現(xiàn)在遇到的是”第 K 大的數(shù)“。區(qū)別要說大也不大,但對(duì)于我們最終選擇的方案可能會(huì)有很大的區(qū)別。

我下面會(huì)介紹一些傳統(tǒng)的 top K 問題的解決思路。并且,按照我一貫的風(fēng)格,肯定會(huì)有代碼放出來,你如果是為了尋找一個(gè)”海量無序數(shù)據(jù)尋找第 K 大的數(shù)“問題的答案,相信你可以直接 copy 我的代碼。

方案一:排序法

排序法是最容易想到的思路,復(fù)雜度為 O(nlogn) 。能夠想到的各類排序算法呼之欲出,快速排序、歸并排序、插入排序、猴子排序...etc

但是工程領(lǐng)域選擇方案,往往不能僅僅使用算法復(fù)雜度來評(píng)估:

  • 每個(gè)排序方案數(shù)據(jù)的交換量
  • 額外空間的申請(qǐng)量
  • 平均復(fù)雜度
  • 最壞復(fù)雜度
  • 不同數(shù)據(jù)量下的表現(xiàn)

那這個(gè)時(shí)候有人就要問了,我該如何選擇合適的方案呢?哎,那我又要提到那句話了,benchmark everything!雖然你肯定知道我最終沒有選擇使用排序來解決第 K 大的問題,但我還是想分享給你我的一些測(cè)試結(jié)論。

在 100w~1000w 數(shù)據(jù)量級(jí)別的無序 long 數(shù)組中,JDK 自帶的 Array.sort() 比任何一個(gè)排序方案都要快。

Array.sort 的內(nèi)部實(shí)現(xiàn)為 timsort,是一種優(yōu)化過后的歸并排序。

排序單純靠想也知道不是最優(yōu)的方案,因?yàn)槲姨岢龅膯栴}中,僅僅需要找到第 K 大的數(shù),排序方案卻興師動(dòng)眾把整個(gè)數(shù)組理順了,沒必要。

方案二:堆

針對(duì)一般的 top K 問題,一般都會(huì)默認(rèn) K 很小,所以一般的 top K 問題,可以選擇使用堆來解決。

堆有個(gè)重要的性質(zhì):每個(gè)結(jié)點(diǎn)的值均不大于其左右孩子結(jié)點(diǎn)的值,則堆頂元素即為整個(gè)堆的最小值。JDK 中 PriorityQueue 實(shí)現(xiàn)了堆這個(gè)數(shù)據(jù)結(jié)構(gòu)堆,通過指定 comparator 字段來表示小頂堆或大頂堆,默認(rèn)為自然序(natural ordering)。

小頂堆解決 Top K 問題的思路:小頂堆維護(hù)當(dāng)前掃描到的最大 K 個(gè)數(shù),其后每一次掃描到的元素,若大于堆頂則入堆,然后刪除堆頂;依此往復(fù),直至掃描完所有元素。Java 實(shí)現(xiàn)第 K 大整數(shù)代碼如下:

  1. public int findKthLargest(int[] nums, int k) { 
  2.   PriorityQueue<Integer> minQueue = new PriorityQueue<>(k); 
  3.   for (int num : nums) { 
  4.     if (minQueue.size() < k || num > minQueue.peek()) 
  5.       minQueue.offer(num); 
  6.     if (minQueue.size() > k) 
  7.       minQueue.poll(); 
  8.   } 
  9.   return minQueue.peek(); 

回到我遇到的問題,求第 K 大的數(shù),這里沒有說明 K 的范圍,那么最壞情況下,K == N/2,無論維護(hù)一個(gè) top K 的小頂堆還是維護(hù)一個(gè) top(N - K) 的大頂堆,都需要占用 O(N/2) 的內(nèi)存,而對(duì)于海量數(shù)據(jù)而言,這顯示是一筆非常大的開銷。所以針對(duì)我比賽的場(chǎng)景,堆的方案可以直接 pass。

堆的解法適用于 K 較小的場(chǎng)景,而且非常方便維護(hù)前 K 個(gè)數(shù)。

方案三:Quick Select

Quick Select 你可能沒聽過,但快速排序(Quick Sort)你肯定有所耳聞,其實(shí)他們兩個(gè)算法的作者都是 Hoare,并且思想也非常接近:選取一個(gè)基準(zhǔn)元素 pivot,將數(shù)組切分(partition)為兩個(gè)子數(shù)組,比 pivot 大的扔左子數(shù)組,比 pivot 小的扔右子數(shù)組,然后遞推地切分子數(shù)組。Quick Select 不同于 Quick Sort 之處在于其沒有對(duì)每個(gè)子數(shù)組做切分,而是對(duì)目標(biāo)子數(shù)組做切分。其次,Quick Select 與Quick Sort 一樣,是一個(gè)不穩(wěn)定的算法;pivot 選取直接影響了算法的好壞,最壞情況下的時(shí)間復(fù)雜度達(dá)到了 O(n2)。

在大學(xué)參加 ACM 時(shí),我便第一次接觸了該算法,記得那時(shí)數(shù)據(jù)量正好卡的 Quick Sort 無法通過,Quick Select 可以通過。

Quick Select 的 Java 實(shí)現(xiàn)如下:

  1. public static long quickSelect(long[] nums, int start, int endint k) { 
  2.         if (start == end) { 
  3.             return nums[start]; 
  4.         } 
  5.         int left = start; 
  6.         int right = end
  7.         long pivot = nums[(start + end) / 2]; 
  8.         while (left <= right) { 
  9.             while (left <= right && nums[left] > pivot) { 
  10.                 left++; 
  11.             } 
  12.             while (left <= right && nums[right] < pivot) { 
  13.                 right--; 
  14.             } 
  15.             if (left <= right) { 
  16.                 long temp = nums[left]; 
  17.                 nums[left] = nums[right]; 
  18.                 nums[right] = temp
  19.                 left++; 
  20.                 right--; 
  21.             } 
  22.         } 
  23.         if (start + k - 1 <= right) { 
  24.             return quickSelect(nums, start, right, k); 
  25.         } 
  26.         if (start + k - 1 >= left) { 
  27.             return quickSelect(nums, leftend, k - (left - start)); 
  28.         } 
  29.         return nums[right + 1]; 

最終,我選擇使用了方案三:Quick Select 作為我求解第 K 大數(shù)的方案,也是 benchmark 下來最快的方案。在 10 次查詢中,排序方案耗時(shí)為 6s,而 Quick Select 方案,僅需要 300ms,可以說是非常大的優(yōu)化。

總結(jié)

本文簡(jiǎn)單介紹了無序數(shù)組求 Top K 問題和無序數(shù)組求第 K 大數(shù)字兩類非常相似的問題,并且提供了常見的三種解決方案。當(dāng)然,該問題也有很多變種,例如在多核機(jī)器,多主機(jī)上求解 TopK,甚至可以引入外排和 MapReduce 的思想,其實(shí)已經(jīng)是在考慮其他層面的優(yōu)化了,我在這里就不過多闡釋了。

 

責(zé)任編輯:武曉燕 來源: Kirito的技術(shù)分享
相關(guān)推薦

2021-07-05 06:39:59

經(jīng)典算法無序數(shù)組

2021-05-17 14:21:48

物聯(lián)網(wǎng)數(shù)據(jù)存儲(chǔ)

2019-10-22 13:54:19

人工智能日志運(yùn)維

2010-04-22 15:34:16

Oracle海量數(shù)據(jù)

2013-03-26 09:25:51

MapReduceHDFS存儲(chǔ)

2017-06-02 16:20:51

MapReduceHDFSDedoop

2013-03-01 10:46:50

大數(shù)據(jù)核心海量數(shù)據(jù)

2018-11-02 11:03:12

2019-08-19 18:42:43

大數(shù)據(jù)海量數(shù)據(jù)

2018-08-22 13:11:35

中數(shù)經(jīng)緯輿情監(jiān)測(cè)中央廚房

2016-08-08 16:10:10

數(shù)據(jù)保護(hù)存儲(chǔ)

2024-01-19 10:50:16

峰會(huì)模型

2021-03-08 10:18:55

數(shù)據(jù)庫(kù)數(shù)據(jù)Prometheus

2021-03-15 10:10:29

數(shù)據(jù)庫(kù)數(shù)據(jù)查詢

2024-02-21 11:51:11

數(shù)據(jù)存儲(chǔ)數(shù)據(jù)壓縮數(shù)據(jù)管理

2017-11-20 11:37:19

時(shí)序數(shù)據(jù)數(shù)據(jù)存儲(chǔ)HBase

2016-09-01 13:25:20

IBM

2017-11-16 19:26:34

海量數(shù)據(jù)算法計(jì)算機(jī)

2011-08-29 14:33:41

2011-04-28 09:36:22

海量數(shù)據(jù)存儲(chǔ)
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)