堆的應用之——求前K最大值和求中值
今天和大家一塊學習下面試中常見的兩個關於堆的面試題,第一:求K個最大值;第二:求中值元素。演算法和資料結構算是筆者的死穴了。
一、求前K個最大的值
首先能想到的是使用Arrays.sort()進行排序後取前K個值即可,效率是O(N*log2^N)。
其次可以進行迴圈選擇K次,每次都從剩下的資料中選擇最大值,效率是O(N*K)。但若K值大於log2^(N),其效率還不如完全排序。
況且上述方法並沒有考慮到資料的動態性,當資料是動態新增的情況下,如何維護前K個值呢?
此時,維護一個長度為K的陣列,陣列中的K個位置填充我們需要的前K個數值,面對源源不斷的資料,都先找到陣列中的最小值,將新來的資料和最小值進行比較,若是大於最小值,則將最小值替換成新數值。那麼問題來了,每來一個數值都需找到陣列中的最小值進行K次比較,會不會略顯繁瑣呢?
上述方法也僅僅是青銅,真正的王者上場了。解決方法是用最小堆互為這K個數值。最小堆,顧名思義,根節點永遠是最小的數值,那麼新來的元素僅需和根節點進行比較即可:小於根節點則pass,否則新值取代根節點,並且向下調整堆,調整的效率為O(log2^K),這樣總體的效率是O(N*log2^K),是不是比上面的方法高效了很多了呢?
Talk is cheap,so codes follow.
前方高能程式碼
class TopK<E> {
private PriorityQueue<E> p;
private int k;
/**
* 構造器.
*/
public TopK(int k) {
this.k = k;
this.p = new PriorityQueue<>(k);
}
/**
* 新增集合中的所有元素.
*/
public void addAll(Collection<? extends E> c) {
for (E e : c) {
add(e);
}
}
public void add(E e) {
// 堆未滿,直接加入
if (p.size() < k) {
p.add(e);
return;
}
Comparable<? super E> heap = (Comparable<? super E>) p.peek();
//新來的小於根節點,不做什麼東西
if (heap.compareTo(e) > 0) {
return;
}
// 新來的大於根節點,把根節點拋棄,加入新來的數值
p.poll();
p.add(e);
}
public <T> T[] toArray(T[] a) {
return p.toArray(a);
}
/**
* 返回第K大的值.
*/
public E getKth() {
return p.peek();
}
}
/**
* Created by Mollychin on 2018/5/28.
*/
public class TopKTest {
public static void main(String[] args) {
TopK<Integer> top5 = new TopK<>(5);
List<Integer> integers = Arrays
.asList(new Integer[]{100, 2, 1, 3, 66, 32, 88, 4, 45, 87, 99, 34, 554, 55, 12, 32});
top5.addAll(integers);
System.out.println("保留5個最大的元素,輸出為:");
System.out.println(Arrays.toString(top5.toArray(new Integer[0])));
System.out.println(top5.getKth());
}
}
輸出結果為:
保留5個最大的元素,輸出為:
[87, 99, 88, 100, 554]
87
二、求中值
一個簡單的思路是排序後取中間的那個值即可,效率是O(N*log2^N)。
但上述方法僅適用於全部數值已知,那麼面對動態新增的數值呢?
可以使用兩個堆,最大堆和最小堆。下面是思路:
- 假設中值是z,則最大堆維護的是小於等於z的數值,最小堆維護的是大於等於z的數值。但是呢,兩個堆都不包含m。
- 當有新的數值n到達的時候,n與z進行比較,按照步驟一的約定將n加入最大堆或者最小堆中。
- 走完第二步後,若最小堆和最大堆的元素個數相差2個或者2個以上,則將m加入元素較少的堆中,然後從元素較多的堆中的根節點移除並且賦值給z。
4.下面我們來看下程式碼實現。
/**
* Created by Mollychin on 2018/5/28.
*/
public class MedianTest {
public static void main(String[] args) {
Median<Integer> integerMedian = new Median<>();
List<Integer> integers = Arrays
.asList(new Integer[]{100, 2, 1, 3, 66, 32, 88, 4, 45, 87, 99, 34, 554, 55, 12, 32});
integerMedian.addAll(integers);
System.out.println("中位數是:"+integerMedian.getM());
}
}
class Median<E> {
private PriorityQueue<E> minHeap;
private PriorityQueue<E> maxHeap;
private E m;
public Median() {
this.minHeap = new PriorityQueue<>();
this.maxHeap = new PriorityQueue<>();
}
private int compare(E e, E m) {
Comparable<? super E> comparable = (Comparable<? super E>) e;
return comparable.compareTo(m);
}
public void add(E e) {
// e為第一個數值時
if (m == null) {
m = e;
return;
}
if (compare(e, m) <= 0) {
// 新來的數值小於等於原中值時則加在最大堆裡
maxHeap.add(e);
} else {
minHeap.add(e);
}
if (minHeap.size() - maxHeap.size() >= 2) {
maxHeap.add(this.m);
this.m = minHeap.poll();
} else if (maxHeap.size() - minHeap.size() >= 2) {
minHeap.add(this.m);
this.m = maxHeap.poll();
}
}
public void addAll(Collection<? extends E> c) {
for (E e : c) {
add(e);
}
}
public E getM() {
return m;
}
}
中位數是:45
總結
相比較排序,使用堆的效率可以大大提高,而且還可以應對資料來源源不斷到來的情景,可以給出實時滿意的結果。這大概就是演算法和資料結構的藝術吧(逃
相關推薦
堆的應用之——求前K最大值和求中值
今天和大家一塊學習下面試中常見的兩個關於堆的面試題,第一:求K個最大值;第二:求中值元素。演算法和資料結構算是筆者的死穴了。 一、求前K個最大的值 首先能想到的是使用Arrays.sort()進行排序後取前K個值即可,效率是O(N*log2^N)。
[LeetCode] Max Sum of Rectangle No Larger Than K 最大矩陣和不超過K
Given a non-empty 2D matrix matrix and an integer k, find the max sum of a rectangle in the matrix such that its sum is no larger than k. Example: Give
(原) mysql 查詢表中最大時間和非空值的合集
有表 查詢 time 最大 con 非空的時間 注:time 為整個表 最大 現有資料 要求結果 時間最大 並且有內容 sql語句select a.id ,a.con, max(b.time) from test a, test b whe
堆處理海量資料----求前k個最小的數--時間複雜度(n * log k)
通過閱讀July的書籍,發現裡面求前k個最小數有很多方法。但在面對處理海量資料處理的時候,不能 把全部資料都放在電腦記憶體中。這時用堆來處理,並把資料放在外存中,通過讀取檔案的方式來讀取。感覺該方法十分巧妙,時間複雜度(n*log k)。程式碼如下: #include&l
PTA-求n以內最大的k個素數以及它們的和(C語言)
輸入樣例1: 1000 10 輸出樣例1: 997+991+983+977+971+967+953+947+941+937=9664 輸入樣例2: 12 6 輸出樣例2: 11+7+5+3+2=28 #include <stdio.h> //判斷素數 int prime(i
7-51 求n以內最大的k個素數以及它們的和 (20 分)
7-51 求n以內最大的k個素數以及它們的和 (20 分) 本題要求計算並輸出不超過n的最大的k個素數以及它們的和。 輸入格式: 輸入在一行中給出n(10≤n≤10000)和k(1≤k≤10)的值。 輸出格式: 在一行中按下列格式輸出: 素數1+素數2+…+素數k
PTA教輔 求n以內最大的k個素數以及它們的和
5-22 求n以內最大的k個素數以及它們的和 (20分) 本題要求計算並輸出不超過n的最大的k個素數以及它們的和。 輸入格式: 輸入在一行中給出n(10≤\le≤n≤\le≤10000)和k(
【演算法之陣列(一)】求子陣列最大和的解決方法詳解
題目: 輸入一個整形陣列,數組裡有正數也有負數。 陣列中連續的一個或多個整陣列成一個子陣列,每個子陣列都有一個和。 求所有子陣列的和的最大值。 例如輸入的陣列為1, -2, 3, 10, -4, 7, 2, -5,和最大的子陣列為3, 10, -4, 7, 2, 因此
求n以內最大的k個素數以及它們的和
import java.util.Scanner; public class sushu { public static void main(String[] args) { Scanner scanf =new Scanner(System.
PTA 7-14 求n以內最大的k個素數以及它們的和(20 分)
本題要求計算並輸出不超過n的最大的k個素數以及它們的和。 輸入格式: 輸入在一行中給出n(10≤n≤10000)和k(1≤k≤10)的值。 輸出格式: 在一行中按下列格式輸出: 素數1+素數2+…+素數k=總和值 其中素數按遞減順序輸出。若n以內不夠k個素數,則按實際個數輸出。 輸入樣例1: 1000
兩個有序陣列,A[k]和B[k]長度都為k。求前k個最小的(a[i]+b[j])
設A={A1,A2,A3,A4,A5,A6,.......} ,B={B1,B2,B3,B4,B5,B6,.......} 因為A和B都是有序的陣列,必須充分的利用這點,可能有同學,看到有同學覺得這個題目比較容易,直接將所有的組合都計算出來,然後取最小的K個,其實出題的人是
我的CUDA學習之旅1——大影象分塊處理程式(包括求均值,最大值等)
引言 在我的第一篇文章中我簡單介紹了CUDA以及我的一些個人學習見解,在本文中我將開始正式開始CUDA實踐之旅,眾做周知CUDA目前應用的領域十分廣泛,它能把一些普通的CPU程式碼提速幾十倍甚至幾百倍。在本人所從事的影象處理領域,在一些大影象的處理上(4K以上
7-51 求n以內最大的k個素數以及它們的和(20 分)
題目: 本題要求計算並輸出不超過n的最大的k個素數以及它們的和。 輸入格式: 輸入在一行中給出n(10≤n≤10000)和k(1≤k≤10)的值。 輸出格式: 在一行中按下列格式輸出: 素數1+素數2+…+素數k=總和值 其中素數按遞減順序輸出
大資料處理堆實現N個數據找K個最大資料和堆排序
在N個數據中找K個最大資料思想:用堆實現找最大的資料,則先建立一個N個數據中其前K個節點的最小堆,將沒進入最小堆的節點依次與小堆的頭節點比較,若大於頭節點,則替換兩個值,並且呼叫向下調整演算法(其思想前面部落格已經介紹實現),直到N個數據比較完成,此時最小堆中的K個節點即為
7-51 求n以內最大的k個素數以及它們的和
7-51 求n以內最大的k個素數以及它們的和(20 分) 本題要求計算並輸出不超過n的最大的k個素數以及它們的和。 輸入格式: 輸入在一行中給出n(10≤n≤10000)和k(1≤k≤10)
中M2018春C入門和進階練習集-程式設計題51 7-51 求n以內最大的k個素數以及它們的和(20 分)
7-51 求n以內最大的k個素數以及它們的和(20 分) 本題要求計算並輸出不超過n的最大的k個素數以及它們的和。 輸入格式: 輸入在一行中給出n(10≤n≤10000)和k(1≤k≤10)的值。 輸出格式: 在一行中按下列格式輸出: 素數1+素數2+…+素數k=
HDU 5407 CRB and Candies(LCM +最大素因子求逆元)
blog std 歸納 get pos http and -a 思路 【題目鏈接】click here~~ 【題目大意】求LCM(Cn0,Cn1,Cn2....Cnn)%MOD 的值 【思路】來圖更直觀: 這個究竟是怎樣推出的,說實話。本人數學歸納大法沒有推出來
MT【16】利用柯西不等式求三角的最大值
技術分享 com style img 不等式 bsp nbsp png 均值 評:此題也可以設$1+cos\theta=t$,平方後變成$t$的單變量利用均值去做. 柯西平衡系數法其實就是待定系數法,利用等號取到的條件。MT【16】利用柯西不等式求三角的最大值
51nod 2006 飛行員配對(二分圖最大匹配) 裸匈牙利算法 求二分圖最大匹配題
spa 解法 tor != cto == 遇到 由於 include 題目: 題目已經說了是最大二分匹配題, 查了一下最大二分匹配題有兩種解法, 匈牙利算法和網絡流。 看了一下覺得匈牙利算法更好理解, 然後我照著小紅書模板打了一遍就過了。 匈牙利算法:先試
C#編程入門--求數組最大值最小值
mar static span int color turn [] return cnblogs 工具類如下: public class ArrayHelper { #region Double數組最大值 ///