1. 程式人生 > >BFPRT演算法(求前k個小的數)-Java實現

BFPRT演算法(求前k個小的數)-Java實現

最近學習了左神BFPRT演算法,給大家先講個段子偷笑

左神說他每次去美國面試,他都會拿BFPRT演算法吹一吹。美國5個大佬在一個美麗的地方研究出來這個演算法,他說自己熱愛演算法,他會BFPRT,每次去美國都會懷著朝聖的姿態去在那個地方轉轉。面試官一聽:哇!生氣 這麼厲害, 過!!!!!!!

好了,下來說說這個演算法。

BFPRT演算法是在進行大量資料排序求topk(前k個最大或最小的數)時最優演算法。

為什麼叫BFPRT演算法,因為是美國5個大佬搞出來的生氣,所以分別取他們的名字命名演算法。

演算法步驟大概如下:

1  將陣列劃分區域,5個數為一組,把陣列分為若干組。(為什麼是5呢?因為5能更好的收斂時間複雜度。)

2  將分開的組進行組內排序(這裡我用的是插入排序),找出每組數的中位數。

3  找出的若干個中位數,單獨放一組進行排序。

4  在找出的中位陣列中,遞迴呼叫步驟2的過程,找出中位陣列的中位數,把這個中位數叫做劃分數。

5  用劃分數進行partition過程(類似於快速排序的每趟的劃分過程):小於劃分數放左邊,等於劃分數放中間,大於劃分數放右邊。

6  將劃分數與k進行比較:

            k<劃分數,在步驟5的左邊區域遞迴上述過程,找出左邊區域的中位數與k比較,直到與k相等,最後的劃分數左邊就是前k小的數;

            k=劃分數,那麼左邊區域直接為前k小的數;

            k>劃分數,在步驟5的右邊區域遞迴上述過程,找出右邊區域的中位數與k比較,直到與k相等,最後的劃分數左邊就是前k小的數;

為什麼要花費這麼多步驟去尋找劃分數呢?假設陣列長度為n,那麼整個陣列一共有n/5箇中位數,在中位陣列找出中位陣列的中位數(劃分數),那麼就會有3*(n/10)個數比劃分數小-如圖紅色區域所示:


我們一次就可以至少刷掉3*(n/10)、最多7*(n/10)的數量級的資料進行遞迴排序,節省了很多時間!

程式碼實現如下所示:

package com.zuoshen;
/** 
 * BFPRT演算法求topk,時間複雜度bO(n)
 *
 * @author wanglongfei    
 * E-mail: 
[email protected]
* @version 2017年8月5日 * */ public class BFPRT { // 得到前k個最小的數 public static int[] getMinKNumsByBFPRT(int[] arr, int k) { if (k < 1 || k > arr.length) { return arr; } int minKth = getMinKthByBFPRT(arr, k); int[] res = new int[k];//res前k個結果集 int index = 0; for (int i = 0; i != arr.length; i++) { if (arr[i] < minKth) { res[index++] = arr[i]; } } for (; index != res.length; index++) { res[index] = minKth; } return res; } //找出比k小的前k個數 public static int getMinKthByBFPRT(int[] arr, int K) { int[] copyArr = copyArray(arr); return select(copyArr, 0, copyArr.length - 1, K - 1); } //複製陣列 public static int[] copyArray(int[] arr) { int[] res = new int[arr.length]; for (int i = 0; i != res.length; i++) { res[i] = arr[i]; } return res; } //用劃分值與k相比,依次遞迴排序 public static int select(int[] arr, int begin, int end, int i) { if (begin == end) { //begin陣列的開始 end陣列的結尾 i表示要求的第k個數 return arr[begin]; } int pivot = medianOfMedians(arr, begin, end);//找出劃分值(中位陣列中的中位數) int[] pivotRange = partition(arr, begin, end, pivot); if (i >= pivotRange[0] && i <= pivotRange[1]) {//小於放左邊,=放中間,大於放右邊 return arr[i]; } else if (i < pivotRange[0]) { return select(arr, begin, pivotRange[0] - 1, i); } else { return select(arr, pivotRange[1] + 1, end, i); } } //找出中位陣列中的中位數 public static int medianOfMedians(int[] arr, int begin, int end) { int num = end - begin + 1; int offset = num % 5 == 0 ? 0 : 1; //分組:每組5個數,不滿5個單獨佔一組 int[] mArr = new int[num / 5 + offset]; //mArr:中位陣列成的陣列 for (int i = 0; i < mArr.length; i++) { //計算分開後各陣列的開始位置beginI 結束位置endI int beginI = begin + i * 5; int endI = beginI + 4; mArr[i] = getMedian(arr, beginI, Math.min(end, endI));//對於最後一組(不滿5個數),結束位置要選擇end } return select(mArr, 0, mArr.length - 1, mArr.length / 2); } //劃分過程,類似於快排 public static int[] partition(int[] arr, int begin, int end, int pivotValue) { int small = begin - 1; int cur = begin; int big = end + 1; while (cur != big) { if (arr[cur] < pivotValue) { swap(arr, ++small, cur++); } else if (arr[cur] > pivotValue) { swap(arr, cur, --big); } else { cur++; } } int[] range = new int[2]; range[0] = small + 1;//比劃分值小的範圍 range[1] = big - 1; //比劃分值大的範圍 return range; } //計算中位數 public static int getMedian(int[] arr, int begin, int end) { insertionSort(arr, begin, end);//將陣列中的5個數排序 int sum = end + begin; int mid = (sum / 2) + (sum % 2); return arr[mid]; } //陣列中5個數排序(插入排序) public static void insertionSort(int[] arr, int begin, int end) { for (int i = begin + 1; i != end + 1; i++) { for (int j = i; j != begin; j--) { if (arr[j - 1] > arr[j]) { swap(arr, j - 1, j); } else { break; } } } } //交換元素順序 public static void swap(int[] arr, int index1, int index2) { int tmp = arr[index1]; arr[index1] = arr[index2]; arr[index2] = tmp; } //列印結果 public static void printArray(int[] arr) { for (int i = 0; i != arr.length; i++) { System.out.print(arr[i] + " "); } System.out.println(); } public static void main(String[] args) { int[] arr = { 6, 9, 1, 3, 1, 2, 2, 5, 6, 1, 3, 5, 9, 7, 2, 5, 6, 1, 9 }; printArray(getMinKNumsByBFPRT(arr, 10)); } }


相關推薦

BFPRT演算法k小的數-Java實現

最近學習了左神BFPRT演算法,給大家先講個段子。 左神說他每次去美國面試,他都會拿BFPRT演算法吹一吹。美國5個大佬在一個美麗的地方研究出來這個演算法,他說自己熱愛演算法,他會BFPRT,每次去美國都會懷著朝聖的姿態去在那個地方轉轉。面試官一聽:哇! 這麼厲害, 過!!

數獨求解演算法回溯法和唯一解法java實現

數獨(すうどく,Sudoku)是一種運用紙、筆進行演算的邏輯遊戲。玩家需要根據9×9盤面上的已知數字,推理出所有剩餘空格的數字,並滿足每一行、每一列、每一個粗線宮內的數字均含1-9,不重複。     注:數獨的各種知識和解決思路請 參考http://www.llang

堆處理海量資料----k最小的數--時間複雜度n * log k

通過閱讀July的書籍,發現裡面求前k個最小數有很多方法。但在面對處理海量資料處理的時候,不能 把全部資料都放在電腦記憶體中。這時用堆來處理,並把資料放在外存中,通過讀取檔案的方式來讀取。感覺該方法十分巧妙,時間複雜度(n*log k)。程式碼如下: #include&l

有序陣列,A[k]和B[k]長度都為kk最小的(a[i]+b[j])

設A={A1,A2,A3,A4,A5,A6,.......} ,B={B1,B2,B3,B4,B5,B6,.......} 因為A和B都是有序的陣列,必須充分的利用這點,可能有同學,看到有同學覺得這個題目比較容易,直接將所有的組合都計算出來,然後取最小的K個,其實出題的人是

LeetCode-347:Top K Frequent Elementsk頻率最高的元素

題目: Given a non-empty array of integers, return the k most frequent elements. 例子: Example: Given [1,1,1,2,2,3] and k = 2, ret

LeetCode703 Kth Largest Element in a StreamK大元素

Design a class to find the kth largest element in a stream. Note that it is the kth largest element in the sorted order, not the kth dis

bzoj2301 [HAOI2011]Problem bgcd==k的個數莫比烏斯反演+容斥原理

首先我們搞掉下界,怎麼搞呢,用容斥原理即可。(看做矩形區間),然後我們需要求∑x=1n∑y=1ngcd(x,y)==k。 ∑x=1⌊n/k⌋∑y=1⌊m/k⌋gcd(x,y)==1 ∑x=1⌊n/k

歸併排序演算法逆序對思路及Java實現

1.歸併排序演算法思路 首先我們要清楚,歸併排序演算法採用了分治法的思想,即將原問題分解為幾個規模較小但類似於原問題的子問題,遞迴地求解這些子問題,然後再合併這些子問題的解來建立原問題的解。歸併排序首先將排序分成兩部分,接著再將這兩部分分解成更小的兩部分,直到分解到只剩一個元素為止。

幾種缺頁中斷演算法FIFO,LRU與LFU實現過程

  最近在做筆試題,其中虛擬儲存管理中幾種缺頁中斷演算法經常考到,雖然這類題可說非常簡單,但概念上卻容易混淆而且如果不掌握正確的做法很容易出錯,因此覺得有必要把這三種演算法的實現過程理一遍,並從原始碼級別去思考它們的實現。  首先推薦一個部落格,對這兩個演算法

PAT乙級——1087陣列操作,輔助空間java實現

題目: 有多少不同的值 (20 分) 當自然數 n 依次取 1、2、3、……、N 時,算式 ⌊ n

LeetCode——中級演算法——排序和搜尋——K高頻元素JavaScript

給定一個非空的整數陣列,返回其中出現頻率前 k 高的元素。 示例 1: 輸入: nums = [1,1,1,2,2,3], k = 2 輸出: [1,2] 示例 2: 輸入: nums = [1], k = 1 輸出: [1] 說明: 你可以假設給定的 k 總是合

【LeetCode題解】347_K高頻元素Top-K-Frequent-Elements

目錄 描述 解法一:排序演算法(不滿足時間複雜度要求) Java 實現 Python 實現 複雜度分析 解法二:最小堆 思路 Java 實現 Python 實現 複雜度分析 解法三:桶排序(bucket s

演算法】反轉佇列K元素

反轉佇列前K個元素 想到佇列的反轉那肯定首先想到的就是堆疊(同理堆疊反轉也可以利用佇列) 有一個辦法我們可以將K個元素入棧,然後可以另一個佇列將剩下的元素放入,隨後我們首先將棧元素迴歸原來的佇列,最後將

KNN演算法---K個數據。

簡介 K Nearest Neighbor演算法又叫KNN演算法,K最近鄰演算法。K表示距離自己最近的k個數據樣本。 個人覺得重點在距離如何表示,如何計算,是簡單的用距離公式,還是用複雜的加權計算。

模擬實現atoi和itoa以及100G 的IP地址出現次數最多的KIP

1.模擬實現C庫的atoi和itoa。 2.給一個超過100G的log file, log中存著IP地址, 設計演算法找到出現次數最多的100個IP地址? 1.題考察面試者的思維方式:完整性和魯棒性 先想好測試用例,溝通好錯誤處理,才能滿意

Leetcode 347:K高頻元素最詳細解決方案!!!

給定一個非空的整數陣列,返回其中出現頻率前 k 高的元素。 例如, 給定陣列 [1,1,1,2,2,3] , 和 k = 2,返回 [1,2]。 注意: 你可以假設給定的 k 總是合理的,1 ≤ k ≤ 陣列中不相同的元素的個數。 你的演算法的時間複雜

計算第K能表示(2^i * 3^j * 5^k)的正整數i,j,k為整數?其7滿足此條件的數分別是1,2,3,4,5,6,8

public class Main { public static void main(String[] args) { int[] a = new int[1501]; a[1] = 1; TreeMap<Integer, Integ

TOP K演算法微軟筆試題 統計英文電子書中出現次數最多的k單詞

        在v_JULY_v的文章中找到了這個問題的解法後用C++實現了一下,發現C++的程式碼非常的簡潔。 主要用到了標準庫中的hash_map,優先順序佇列priority_queue。  

歐幾里德演算法正整數的最大公約數

  getchar()會接受前一個scanf的回車符 */ #include<stdio.h> void main() {     int temp;     int a,b;     s

遞迴演算法n的加法組合,將一個整數拆分成多整數相加的形式, O(N)時間,O(N)空間

網上的多種解法比較複雜,本文用遞迴方法,22行程式碼搞定。時間和空間複雜度已經降到最低! 第三版:加入創作思路。 這個函式的主要功能就是輸出所有組合。既然是輸出所有的組合,那就意味著內部有一個遍歷所有組合的過程。既然是遍歷,而且是O(N)時間,那就說明這個遍歷是按照某種輸出次序,從“第一個組合”遍歷到