1. 程式人生 > >Top K問題的兩種解決思路

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、看看牛人是怎麼說的,為什麼多執行緒併發是不安全的? 在作業系統中,執行緒是不再擁有資源的,程序是擁有資源的。而執行緒是由程序建立的