日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

29 | 堆的应用:如何快速获取到Top 10最热门的搜索关键词?

發布時間:2023/12/10 编程问答 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 29 | 堆的应用:如何快速获取到Top 10最热门的搜索关键词? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

為什么評價算法性能是根據時間和空間復雜度,而不是別的參數?是因為計算機結構是馮諾依曼體系,除了輸入輸出設備和控制器,就剩下運算器和存儲器了

問題引入

搜索引擎的熱門搜索排行榜功能是如何實現的?搜索引擎每天會接收大量的用戶搜索請求,把這些用戶輸入的搜索關鍵詞記錄下來,然后再離線地統計分析,得到最熱門的 Top 10 搜索關鍵詞。假設現在我們有一個包含 10 億個搜索關鍵詞的日志文件,如何能快速獲取到熱門榜 Top 10 的搜索關鍵詞呢

堆這種數據結構幾個非常重要的應用:優先級隊列、求 Top K 和求中位數

堆的應用一:優先級隊列

優先級隊列

堆的核心功能就是取出堆頂數據!(大頂堆--最大值;小頂堆--最小值)。 使用方法:

1.插入數據,與堆頂比較決定插入與否(比如小頂堆堆頂拿到最小值,若新插入數據比它大,就刪掉堆頂最小值,插入新數據,從而堆中保留了TopK);

2.取數據,取最大值/最小值(如優先隊列的優先級數值就是大頂堆堆頂即最大值,入隊后自下而上堆化得到最新的堆頂,出隊時直接出堆頂即可。)

1、優先級隊列中,數據的出隊順序不是先進先出,而是按照優先級來,優先級最高的,最先出隊

2、用堆來實現是最直接、最高效的

3、往優先級隊列中插入一個元素,就相當于往堆中插入一個元素;從優先級隊列中取出優先級最高的元素,就相當于取出堆頂元素。

合并有序小文件

假設我們有 100 個小文件,每個文件的大小是 100MB,每個文件中存儲的都是有序的字符串。我們希望將這些 100 個小文件合并成一個有序的大文件。這里就會用到優先級隊列。

方案一

1、使用一個數組,從這100個文件中各自取出第一個字符串放入數組比較大小,然后將最小的放入合并后的文件中,并從數組中刪除。

2、假設最小的字符串來自A文件,那么再從A文件中取出第二個字符串放入比較數組中重新比較大小

3、重復執行步驟1,直到所有文件中數據都放入合并后的大文件

缺點:每次從數組中取最小字符串,都需要循環遍歷整個數組

方案二

使用優先級隊列(堆)

1、從100個文件中取出第一個元素放到小頂堆中,堆頂元素也即優先級隊列隊首就是最小字符串。將該字符串放入到合并后最終大文件中,并從堆頂部刪除

2、再從小文件中取出下一個元素放入堆頂,循環該過程

3、刪除堆頂元素和堆中插入元素的復雜度都是O(logn)

?

高性能的定時器

假設有一個定時器,定時器中維護了很多定時任務,每個任務都設定了一個要觸發執行的時間點。定時器每過一個很小的單位時間(比如 1 秒),就掃描一遍任務,看是否有任務到達設定的執行時間。如果到達了,就拿出來執行

這種方式的缺點:每過 1 秒就掃描一遍任務列表的做法比較低效,第一,任務的約定執行時間離當前時間可能還有很久,這樣前面很多次掃描其實都是徒勞的;第二,每次都要掃描整個任務列表,如果任務列表很大的話,勢必會比較耗時。

優化方案

  • 按照任務設定的執行時間,將這些任務存儲在優先級隊列中,隊列首部(也就是小頂堆的堆頂)存儲的是最先執行的任務。
  • 拿隊首任務的執行時間點,與當前時間點相減,得到一個時間間隔 T
  • 定時器就可以設定在 T 秒之后,再來執行任務。從當前時間點到(T-1)秒這段時間里,定時器都不需要做任何事情
  • ?T 秒時間過去之后,定時器取優先級隊列中隊首的任務執行。然后再計算新的隊首任務的執行時間點與當前時間點的差值,把這個值作為定時器執行下一個任務需要等待的時間

利用堆求Top K

一類:靜態數據集合

  • 維護一個大小為 K 的小頂堆,順序遍歷數組,從數組中取出數據與堆頂元素比較
  • 如果比堆頂元素大,把堆頂元素刪除,并且將這個元素插入到堆中;如果比堆頂元素小,則不做處理,繼續遍歷數組
  • 最后堆中的數據就是前K大數據

遍歷數組需要 O(n) 的時間復雜度,一次堆化操作需要 O(logK) 的時間復雜度,所以最壞情況下,n 個元素都入堆一次,時間復雜度就是 O(nlogK)

二類:動態數據集合

一個數據集合中有兩個操作,一個是添加數據,另一個詢問當前的前 K 大數據。如果每次詢問前 K 大數據,我們都基于當前的數據重新計算的話,那時間復雜度就是 O(nlogK),n 表示當前的數據的大小;

方案

  • 實際上,可以一直維護一個 K 大小的小頂堆,當有數據被添加到集合中時,與堆頂的元素對比。
  • 如果比堆頂元素大,把堆頂元素刪除,并且將這個元素插入到堆中;如果比堆頂元素小,則不做處理。
  • 無論任何時候需要查詢當前的前 K 大數據,都可以立刻返回給他

利用堆求中位數

如何求動態數據集合中的中位數:中位數就是處在中間位置的那個數。如果數據的個數是奇數,把數據從小到大排列,那第 2n?+1 個數據就是中位數(注意:假設數據是從 0 開始編號的);如果數據的個數是偶數的話,那處于中間位置的數據有兩個,第 2n? 個和第 2n?+1 個數據,這個時候可以隨意取一個作為中位數,比如取兩個數中靠前的那個,就是第 2n? 個數據

靜態數據

對于靜態數據,中位數是固定的。可以先排序,第 2n? 個數據就是中位數。每次詢問中位數的時候,直接返回這個固定的值。所以,盡管排序的代價比較大,但是邊際成本會很小。

動態數據

動態數據集合,中位數在不停地變動。

需要維護兩個堆,一個大頂堆,一個小頂堆。大頂堆中存儲前半部分數據,小頂堆中存儲后半部分數據,且小頂堆中的數據都大于大頂堆中的數據。如果有 n 個數據,n 是偶數,我們從小到大排序,那前 2n? 個數據存儲在大頂堆中,后 2n? 個數據存儲在小頂堆中。這樣,大頂堆中的堆頂元素就是我們要找的中位數。如果 n 是奇數,情況是類似的,大頂堆就存儲 2n?+1 個數據,小頂堆中就存儲 2n? 個數據

當添加一個數據時候:如何調整兩個堆,使得大頂堆中的堆頂數據繼續是中位數呢

  • 如果新加入的數據小于等于大頂堆的堆頂元素,將這個新數據插入到大頂堆;否則,將這個新數據插入到小頂堆
  • 可能情況:如果 n 是偶數,兩個堆中的數據個數都是 2n?;如果 n 是奇數,大頂堆有 2n?+1 個數據,小頂堆有 2n? 個數據
  • 這時候需要從一個堆中不停地將堆頂元素移動到另一個堆,通過這樣的調整,來讓兩個堆中的數據滿足上面的約定
  • 最終求中位數只需要返回大頂堆的堆頂元素就可以了,所以時間復雜度就是 O(1)。

如何快速求接口的 99% 響應時間?

99% 響應時間。如果有 100 個接口訪問請求,每個接口請求的響應時間都不同,比如 55 毫秒、100 毫秒、23 毫秒等,我們把這 100 個接口的響應時間按照從小到大排列,排在第 99 的那個數據就是 99% 響應時間,也叫 99 百分位響應時間。

利用兩個堆實現。一個大頂堆,一個小頂堆。假設當前總數據的個數是 n,大頂堆中保存 n*99% 個數據,小頂堆中保存 n*1% 個數據。大頂堆堆頂的數據就是我們要找的 99% 響應時間。方法類似前面求中位數

解答開頭

一個包含 10 億個搜索關鍵詞的日志文件,如何快速獲取到 Top 10 最熱門的搜索關鍵詞呢

多臺機器可以采用map-reduce的解決方法

單機環境:

方案一

1、首先統計每個關鍵詞出現頻率

2、采用散列表,順序掃描10億個關鍵詞,存在次數+1,不存在記錄次數為1;遍歷完成后散列表中就存儲了不重復關鍵詞以及出現次數

3、利用堆求Top K的方法,建立一個小頂堆大小為10,遍歷散列表,然后從散列表匯中依次取出每個搜索關鍵詞以及對應的出現次數,與堆頂中的搜索關鍵詞進行對比。大就刪除堆頂關鍵詞替換為新的關鍵詞,小于則不作處理

缺點:如果每個搜索關鍵詞的平均長度是 50 個字節,那存儲 1 億個關鍵詞起碼需要 5GB 的內存空間,散列表因為要避免頻繁沖突,不會選擇太大的裝載因子,所以消耗的內存空間就更多了。如果機器只有1G內存空間,那么無法一次性將所有關鍵詞加入內存,

方案二

1、創建 10 個空文件 00,01,02,……,09

2、遍歷這 10 億個關鍵詞,通過哈希算法對其求哈希值,然后哈希值同 10 取模,得到的就是搜索關鍵詞應該被分到的文件編號

3、10 億個關鍵詞分片之后,每個文件都只有 1 億的關鍵詞,去除掉重復的,可能就只有 1000 萬個,每個關鍵詞平均 50 個字節,所以總的大小就是 500MB。1GB 的內存完全可以放得下

4、每個包含 1 億條搜索關鍵詞的文件,利用散列表和堆,分別求出 Top 10,然后把這個 10 個 Top 10 放在一塊,然后取這 100 個關鍵詞中,出現次數最多的 10 個關鍵詞,這就是這 10 億數據中的 Top 10 最頻繁的搜索關鍵詞

總結

優先級隊列是一種特殊的隊列,優先級高的數據先出隊,而不再像普通的隊列那樣,先進先出。堆就可以看作優先級隊列,只是稱謂不一樣罷了。

求 Top K 問題又可以分為針對靜態數據和針對動態數據,只需要利用一個堆,就可以做到非常高效率地查詢 Top K 的數據。

求中位數實際上還有很多變形,比如求 99 百分位數據、90 百分位數據等,處理的思路都是一樣的,即利用兩個堆,一個大頂堆,一個小頂堆,隨著數據的動態添加,動態調整兩個堆中的數據,最后大頂堆的堆頂元素就是要求的數據。

思考

有一個訪問量非常大的新聞網站,我們希望將點擊量排名 Top 10 的新聞摘要,滾動顯示在網站首頁 banner 上,并且每隔 1 小時更新一次。如果你是負責開發這個功能的工程師,你會如何來實現呢?

方案一1、實時建立散列表,key是新聞的摘要,value是點擊量;
2、建立一個10的小頂堆,每隔一個小時掃描一次散列表,根據點擊量大小放入到小頂堆中,掃描完散列表后即出現Top10 的新聞點擊量。
方案二1,對每篇新聞摘要計算一個hashcode,并建立摘要與hashcode的關聯關系,使用map存儲,以hashCode為key,新聞摘要為值
2,按每小時一個文件的方式記錄下被點擊的摘要的hashCode
3,當一個小時結果后,上一個小時的文件被關閉,開始計算上一個小時的點擊top10
4,將hashcode分片到多個文件中,通過對hashCode取模運算,即可將相同的hashCode分片到相同的文件中
5,針對每個文件取top10的hashCode,使用Map<hashCode,int>的方式,統計出所有的摘要點擊次數,然后再使用小頂堆(大小為10)計算top10,
6,再針對所有分片計算一個總的top10,最后合并的邏輯也是使用小頂堆,計算top10
7,如果僅展示前一個小時的top10,計算結束
8,如果需要展示全天,需要與上一次的計算按hashCode進行合并,然后在這合并的數據中取top10
9,在展示時,將計算得到的top10的hashcode,轉化為新聞摘要顯示即可
方案三

1,維護兩個散列表,一個是一小時新增的點擊量的散列表,以新聞id為鍵,點擊次數為值。一個是全部點擊量的散列表。每隔一小時把新增的散列表的數據同步到全部點擊量的散列表。然后把這小時內有變化的全部點擊量的散列表的數據(即此小時有新增點擊量的新聞數據)和我們維護的10個元素小頂堆堆頂進行比較,比堆頂的點擊量大的,則使用該元素替換堆頂,再進行堆化。比堆頂點擊量小的則不做處理。然后比較完,根據堆頂的10個元素的id,從數據庫讀取相應的新聞摘要顯示在banner上。除此之外,還要把變化后的全部點擊量散列表同步到數據庫。因為保存的是新聞id,所以散列表長度不會很大,所占用的內存也不會很大。而每個小時新增的訪問量的新聞id數也不會很多,畢竟很多人只會閱讀熱門消息。所以新增的點擊量的新聞數據假設為k,則每小時同步小頂堆的時間負責度為o(klg 10);

2018-12-02

?

總結

以上是生活随笔為你收集整理的29 | 堆的应用:如何快速获取到Top 10最热门的搜索关键词?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。