在處理大量數據的時候,有時候每每須要找出Top前幾的數據,這時候若是直接對數據進行排序,在處理海量數據的時候每每就是不可行的了,並且在排序最好的時間複雜度爲nlogn,當n遠大於須要獲取到的數據的時候,時間複雜度就顯得太高。
使用最小堆或者最大堆能夠很好地解決Top大問題或者Top小問題。java
對於n個數,取Top m個數,時間複雜度爲O(nlogm),這樣在n較大狀況下,是優於nlogn的時間複雜度的。數據結構
好比10000個數據,取前100大的數,那麼時間複雜度就是O(10000log100)。
由於在插入數據的時候須要遍歷元素時間複雜度達到了O(10000),而後每次插入過程當中進行調整的複雜度爲O(log100),因此整體時間複雜度爲O(10000log100)。ide
Java集合中的PriorityQueue就能夠實現最大堆或者最小堆,從名字能夠知道該集合是優先隊列,數據結構中的優先隊列就是使用堆來實現的。函數
// 底層經過一個Object類型數據保存元素 transient Object[] queue; // 經過Comparator制定比較方法 private final Comparator<? super E> comparator; // 其中一個構造函數 public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) { // Note: This restriction of at least one is not actually needed, // but continues for 1.5 compatibility if (initialCapacity < 1) throw new IllegalArgumentException(); this.queue = new Object[initialCapacity]; this.comparator = comparator; }
下面就使用PriorityQueue來實現最小堆和最大堆。this
public class TopK<E extends Comparable> { private PriorityQueue<E> queue; private int maxSize; //堆的最大容量 public TopK(int maxSize) { if (maxSize <= 0) { throw new IllegalStateException(); } this.maxSize = maxSize; this.queue = new PriorityQueue<>(maxSize, new Comparator<E>() { @Override public int compare(E o1, E o2) { // 最大堆用o2 - o1,最小堆用o1 - o2 return (o1.compareTo(o2)); } }); } public void add(E e) { if (queue.size() < maxSize) { queue.add(e); } else { E peek = queue.peek(); if (e.compareTo(peek) > 0) { queue.poll(); queue.add(e); } } } public List<E> sortedList() { List<E> list = new ArrayList<>(queue); Collections.sort(list); return list; } public static void main(String[] args) { int[] array = {4, 5, 1, 6, 2, 7, 3, 8}; TopK pq = new TopK(4); for (int n : array) { pq.add(n); } System.out.println(pq.sortedList()); } }
運行結果:
rest
經過上述講述,基本瞭解最大堆和最小堆狀況以及它們與TopK問題的關係,上面是使用集合實現,下面使用Java來實現最小堆,並解決TopK大問題。code
public class TopK { int[] items; int currentSize = 0; // 初始化爲size + 1,從下標1開始保存元素。 public TopK(int size) { items = new int[size + 1]; } // 插入元素 public void insert(int x) { if (currentSize == items.length - 1) { if (compare(x, items[1]) < 0) { return; } else if (compare(x, items[1]) > 0) { deleteMin(); } } int hole = ++currentSize; for (items[0] = x; compare(x, items[hole / 2]) < 0; hole /= 2) { items[hole] = items[hole / 2]; } items[hole] = x; } // 刪除最小堆中最小元素 public int deleteMin() { int min = items[1]; items[1] = items[currentSize--]; percolateDown(1); return min; } // 下濾 public void percolateDown(int hole) { int child; int temp = items[1]; for (; hole * 2 <= currentSize; hole = child) { child = 2 * hole; if (child != currentSize && compare(items[child + 1], items[child]) == -1) { child++; } if (compare(items[child], temp) < 0) { items[hole] = items[child]; } else { break; } } items[hole] = temp; } // 制定比較規則 public static int compare(int a, int b) { if (a < b) { return -1; } else if (a > b) { return 1; } return 0; } public static void main(String[] args) { TopK topK = new TopK(10); for (int i = 1; i <= 100; i++) { topK.insert(i); } for (int j = 1; j <= topK.currentSize; j++) { System.out.print(topK.items[j] + " "); } System.out.println(); } }
運行結果:
blog