Top K問題的兩種解決思路
Top K問題在資料分析中非常普遍的一個問題(在面試中也經常被問到),比如:
從20億個數字的文字中,找出最大的前100個。
解決Top K問題有兩種思路,
- 最直觀:小頂堆(大頂堆 -> 最小100個數);
- 較高效:Quick Select演算法。
1. 堆
小頂堆(min-heap)有個重要的性質——每個結點的值均不大於其左右孩子結點的值,則堆頂元素即為整個堆的最小值。JDK中PriorityQueue
實現了資料結構堆,通過指定comparator
欄位來表示小頂堆或大頂堆,預設為null,表示自然序(natural ordering)。
小頂堆解決Top K問題的思路:小頂堆維護當前掃描到的最大100個數,其後每一次的掃描到的元素,若大於堆頂,則入堆,然後刪除堆頂;依此往復,直至掃描完所有元素。Java實現第K大整數程式碼如下:
public int findKthLargest(int[] nums, int k) { PriorityQueue<Integer> minQueue = new PriorityQueue<>(k); for (int num : nums) { if (minQueue.size() < k || num > minQueue.peek()) minQueue.offer(num); if (minQueue.size() > k) minQueue.poll(); } return minQueue.peek(); }
2. Quick Select
Quick Select [1]脫胎於快排(Quick Sort),兩個演算法的作者都是Hoare,並且思想也非常接近:選取一個基準元素pivot,將陣列切分(partition)為兩個子陣列,比pivot大的扔左子陣列,比pivot小的扔右子陣列,然後遞推地切分子陣列。Quick Select不同於Quick Sort的是其沒有對每個子陣列做切分,而是對目標子陣列做切分。其次,Quick Select與Quick Sort一樣,是一個不穩定的演算法;pivot選取直接影響了演算法的好壞,worst case下的時間複雜度達到了\(O(n^2)\)。下面給出Quick Sort的Java實現:
public void quickSort(int arr[], int left, int right) {
if (left >= right) return;
int index = partition(arr, left, right);
quickSort(arr, left, index - 1);
quickSort(arr, index + 1, right);
}
// partition subarray a[left..right] so that a[left..j-1] >= a[j] >= a[j+1..right]
// and return index j
private int partition(int arr[], int left, int right) {
int i = left, j = right + 1, pivot = arr[left];
while (true) {
while (i < right && arr[++i] > pivot)
if (i == right) break;
while (j > left && arr[--j] < pivot)
if (j == left) break;
if (i >= j) break;
swap(arr, i, j);
}
swap(arr, left, j); // swap pivot and a[j]
return j;
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
Quick Select的目標是找出第k大元素,所以
- 若切分後的左子陣列的長度 > k,則第k大元素必出現在左子陣列中;
- 若切分後的左子陣列的長度 = k-1,則第k大元素為pivot;
- 若上述兩個條件均不滿足,則第k大元素必出現在右子陣列中。
Quick Select的Java實現如下:
public int findKthLargest(int[] nums, int k) {
return quickSelect(nums, k, 0, nums.length - 1);
}
// quick select to find the kth-largest element
public int quickSelect(int[] arr, int k, int left, int right) {
if (left == right) return arr[right];
int index = partition(arr, left, right);
if (index - left + 1 > k)
return quickSelect(arr, k, left, index - 1);
else if (index - left + 1 == k)
return arr[index];
else
return quickSelect(arr, k - index + left - 1, index + 1, right);
}
上面給出的程式碼都是求解第k大元素;若想要得到Top K元素,僅需要將程式碼做稍微的修改:比如,掃描完成後的小頂堆對應於Top K,Quick Select演算法用中間變數儲存Top K元素。
3. 參考資料
[1] Hoare, Charles Anthony Richard. "Algorithm 65: find." Communications of the ACM 4.7 (1961): 321-322.
[2] James Aspnes, QuickSelect.
相關推薦
Top K問題的兩種解決思路
Top K問題在資料分析中非常普遍的一個問題(在面試中也經常被問到),比如: 從20億個數字的文字中,找出最大的前100個。 解決Top K問題有兩種思路, 最直觀:小頂堆(大頂堆 -> 最小100個數); 較高效:Quick Select演算法。 1. 堆 小頂堆(min-heap)有個重要
mysql遠程連接失敗的兩種解決方法
mysql password upd leg .cn 權限 連接 每次 ddr ---恢復內容開始--- (這是轉載別人的,因為我覺得很有用,每次都是參考這個的第二種方法解決的,不管你聽不聽得到,先說聲謝謝!也記下來方便大家看看) mysql解決遠程不能訪問的二種方法,需要
啟動MongoDB報version `OPENSSL_1.0.2‘ not found的兩種解決辦法
x86_64 crypt 原來 版本問題 原因分析 ubun 生效 grep openssl命令 問題描述: 在部署MongoDB的時候,啟動過程中有遇到“version `OPENSSL_1.0.2‘ not found ”這樣的報錯,導致MongoDB服務起不來: [r
VLOOKUP函數返回錯誤值#N/A的兩種解決方法
ask 可能 html 截圖 沒有 class b- http 工資 來源:http://www.ittribalwo.com/article/3626.html 下面的截圖,根據左邊的工號查詢相應的工資。小夥伴的F2單元格公式是:=VLOOKUP(E2,$A$1:$C
Linux遺忘root密碼的其中兩種解決方法
.com sha 一個 需要 shadow ada 操作系統 分享 http 由於安全的需要,系統一般只有一個root用戶,因此若遺忘root用戶的登錄密碼,因此需要通過其他途徑進行修改密碼。1.通過單用戶模式(1)重啟系統,按任意鍵進入grub菜單。出現grub菜單時,按
linux 命令行不顯示路徑了,而顯示為-bash-4.1#的兩種解決辦法
技術分享 用戶家目錄 清空 目錄 RoCE color 編輯 重新 我們 問題描述: linux的命令行界面顯示的不是路徑,而是-bash-4.1#: 原因分析: 出現這個問題的原因是因為沒有配置.bash_profile的問題,或者是我們不小心清空或刪除了.bash_pr
MySQL同步故障:" Slave_SQL_Running:No" 兩種解決辦法
MySQL同步故障:" Slave_SQL_Running:No" 兩種解決辦法 使用中出現了這種情況,經過一番查詢,終於解決 首先停掉Slave服務:slave stop 到主伺服器上檢視主機狀態: 記錄File和Position對應的值
分散式事務有兩種解決方式
1.優先使用非同步訊息。 上文已經說過,使用非同步訊息 Consumer 端需要實現冪等。冪等有兩種方式,一種方式是業務邏輯保證冪等。比如接到支付成功的訊息訂單狀態變成支付完成,如果當前狀態是支付完成,則再收到一個支付成功的訊息則說明訊息重複了,直接作為訊息成功處理。 另外一種方式如果業務邏輯無
SQL三表連線的兩種不同思路
之前寫SQL一直都是按照基本的思路 select from 表1 LEFT JOIN where。。。 今天看到別人的SQL ,雖然新方法更復雜,但是確實開闊了思路。 step1 set宣告變數 step2 方法一: SELECT T1…, T2… FROM (SELECT 列1,列2… F
Slave_SQL_Running:No的兩種解決辦法
進入slave伺服器,執行: mysql> show slave status\G ....... Relay_Log_File: localhost-relay-bin.000535 Relay_Log_Pos: 217
linux 命令列不顯示路徑了,而顯示為-bash-4.1#的兩種解決辦法
問題描述: linux的命令列介面顯示的不是路徑,而是-bash-4.1#: 原因分析: 出現這個問題的原因是因為沒有配置.bash_profile的問題,或者是我們不小心清空或刪除了.bash_profile檔案。 解決方法 方法一:修改 ~/.bash_profile檔案 1、修改~/.bas
SQL Server 2017安裝錯誤:Polybase要求安裝Oracle JRE 7更新51或更高版本的兩種解決方法
安裝SQL Server 2017遇到的問題: 第一種方法是安裝jdk7,但是現在官方已經不提供下載了,我手邊只有jdk-x86,與我的部分軟體不相容,所以果斷放棄。索性採取第二種方式,等到以後需要Polybase再裝也不遲。 先看問題情況: 第一種解決方法: 也就是大家都推薦的,下
MyBatis - 實體類的屬性名和資料庫列名不一致時的兩種解決辦法!
問題:兩者不一致時 , 查詢結果無法封裝到實體!(也就無法查詢出來) ① 查詢的sql語句中使用別名進行查詢. 但要注意: 欄位名的別名 要和 實體類的屬性名一致! UserMapper.xml <!-- namespace:介面的全路徑名.
Python中json.loads()無法解析單引號字串問題的兩種解決方法
目錄 1、json檔案的儲存與載入 2、json.loads()無法解析單引號字串問題 3、解決方案 方案一:替換單引號 方案二:在使用json.loads()前使用eval()和json.dumps()進行處理 1、json檔案的儲存與載入 一般來說,我建立字典、儲
數獨遊戲的兩種程式設計思路+程式碼
###數獨 方法一: 設定三個方法;分別為行不重複,列不重複,單元格不重複;在判斷是否重複的時候用了一個Boolean陣列,預設值為false,若角標位置為true時那麼說明已經重複了 需求:判斷是否為數獨矩陣 /* 思路:當每行元素不得重複,並且每列元素不得重複,並且每個小方陣也不得
**Bean named 'XXX' is expected but was actually of type 'com.sun.proxy.$Proxy**'的兩種解決方法**
but was actually of type 'com.sun.proxy.$Proxy’的兩種解決方法** 錯誤提示: Caused by: org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean
day028兩種粘包現象,兩種解決粘包的方法,subprocess, struck模組
本節內容: 1.兩種粘包現象 2.struck模組的使用 3.兩種解決粘包的解決方案 4.驗證客戶端的連結合法性 參考文章:https://www.cnblogs.com/clschao/articles/9593164.html?tdsourcetag=s_pctim_aiomsg#part_9 一、兩
Linux執行 wget命令:提示command not found的兩種解決方法
1、rpm 安裝 下載wget的RPM包: http://mirrors.163.com/centos/6.8/os/x86_64/Packages/wget-1.12-8.el6.x86_64.rpm 執行 rpm -ivh wget-1.12-8.el6.x86_64.rpm2、yu
斐波那契數列的兩種解題思路:遞迴VS迭代
一、問題描述 要求輸入一個整數n,請你輸出斐波那契數列的第n項 二、演算法分析 給出一系列斐波拉契數列:0 1 1 3 5 8 13 21 。。。 通過觀察,很容易發現: 1 n=0,
Java之多執行緒安全(屌絲版,兩大解決思路,要麼不去競爭(開闢執行緒副本)、要麼有順序的競爭資源(用鎖規定執行緒秩序))
0、多執行緒安全,如果多個執行緒操作一個變數,每次都能達到預期的結果,那麼說明當前這個類起碼是執行緒安全的,我這白話的,可能有點噁心。 1、看看牛人是怎麼說的,為什麼多執行緒併發是不安全的? 在作業系統中,執行緒是不再擁有資源的,程序是擁有資源的。而執行緒是由程序建立的