1. 程式人生 > >堆的應用之——求前K最大值和求中值

堆的應用之——求前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)。
但上述方法僅適用於全部數值已知,那麼面對動態新增的數值呢?
可以使用兩個堆,最大堆和最小堆。下面是思路:

  1. 假設中值是z,則最大堆維護的是小於等於z的數值,最小堆維護的是大於等於z的數值。但是呢,兩個堆都不包含m。
  2. 當有新的數值n到達的時候,n與z進行比較,按照步驟一的約定將n加入最大堆或者最小堆中。
  3. 走完第二步後,若最小堆和最大堆的元素個數相差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]長度都為kk小的(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數組最大值 ///