我今天在处理topk问题的时候需要一个堆容器,翻了半天资料没找到,最后偶然看到了这张图:
啧啧啧,这张图上居然没有堆(heap)。
好像java中真的没有听说过堆这么个容器……
只好自己动手丰衣足食了
接口:
/** * */ package com.lille.tool.heap; import java.util.Collection; import java.util.NoSuchElementException; /** * * @author Lille qliujinming@qq.com * @time 2018年4月1日 下午7:31:07 */ public interface Heap<T extends Comparable<T>> extends Collection<T> { int size(); /** * 取得但并不删除堆顶元素,返回null,如果这个堆是空的 <br/> * Retrieves, but does not remove, the head of this Heap, or returns * {@code null} if this Heap is empty. * * @return 堆顶元素<br/> * the head of this heap, or {@code null} if this heap is empty * */ T peek(); /** * 取得但不删除堆顶元素,这个方法和peek的唯一区别是当堆为空时抛出一个NoSuchElementException <br/> * Retrieves, but does not remove, the top of this Heap. This method differs * from {@link #peek peek} only in that it throws an exception if this Heap * is empty. * * @return 堆顶元素<br/> * the top of this Heap * @throws NoSuchElementException * 当且仅当堆是空的 <br/> * if this Heap is empty */ T element(); /** * 返回并删除堆顶元素,该方法和poll方法唯一的不同是当堆为空时抛出异常 * * <br/> * Retrieves and removes the top of this heap. This method differs from * {@link #poll poll} only in that it throws an exception if this heap is * empty. * * @return 堆顶元素<br/> * the top of this heap * @throws NoSuchElementException * 当且仅当堆为空时 <br/> * if this heap is empty */ T remove(); /** * 返回并删除堆顶元素<br/> * Retrieves and removes the top of the heap, or returns {@code null} if * this heap is empty. * * @return 堆顶元素<br/> * the top of the heap, or {@code null} if this heap is empty */ T poll(); /** * 插入特定元素到这个堆中,该方法不同于{@link #add add}的地方在于堆满时返回false,而不抛出异常 <br/> * Inserts the specified element into this heap if it is possible to do so * immediately without violating capacity restrictions.The method differs * from {@link #add add} only in that it return false when there are no * place to insert the element but not throw a exception. * * @param t * 需要插入的元素<br/> * the specified element * @return 插入成功则返回true,否则返回false<br/> * {@code true} if the element was insert to this heap, else * {@code false} */ boolean insert(T t); boolean add(T t); }
实现:
最大堆:
/** * */ package com.lille.tool.heap; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; /** * 最大堆 * * @author Lille qliujinming@qq.com * @time 2018年4月1日 下午7:31:07 */ public class MaxHeapImpl<T extends Comparable<T>> implements Heap<T> { private ArrayList<T> heap; public MaxHeapImpl() { heap=new ArrayList<T>(); } public MaxHeapImpl(Collection<? extends T> c) { heap=new ArrayList<T>(c.size()+1); for (T t : c) insert(t); } /* * (非 Javadoc) * * @see java.util.Collection#isEmpty() */ @Override public boolean isEmpty() { // TODO 自动生成的方法存根 return heap.isEmpty(); } /* * (非 Javadoc) * * @see java.util.Collection#contains(java.lang.Object) */ @Override public boolean contains(Object o) { // TODO 自动生成的方法存根 return heap.contains(o); } /* * (非 Javadoc) * * @see java.util.Collection#iterator() */ @Override public Iterator<T> iterator() { // TODO 自动生成的方法存根 return new Itr(); } private class Itr implements Iterator<T> { int cursor = 0; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such /* * (非 Javadoc) * * @see java.util.Iterator#hasNext() */ @Override public boolean hasNext() { // TODO 自动生成的方法存根 return cursor < heap.size(); } /* * (非 Javadoc) * * @see java.util.Iterator#next() */ @Override public T next() { // TODO 自动生成的方法存根 lastRet = cursor; return heap.get(cursor++); } @Override public void remove() { if(lastRet<0)throw new RuntimeException("lastRet"+lastRet); MaxHeapImpl.this.remove(lastRet); lastRet=-1; cursor--; } } /* * (非 Javadoc) * * @see java.util.Collection#toArray() */ @Override public Object[] toArray() { // TODO 自动生成的方法存根 return heap.toArray(); } /* * (非 Javadoc) * * @see java.util.Collection#toArray(java.lang.Object[]) */ @Override public <E> E[] toArray(E[] a) { // TODO 自动生成的方法存根 return heap.toArray(a); } /* * (非 Javadoc) * * @see java.util.Collection#remove(java.lang.Object) */ @Override public boolean remove(Object o) { // TODO 自动生成的方法存根 int flag = heap.size(); for (int i = 0; i < heap.size(); i++) { if (heap.get(i).equals(o)) { remove(i); i--; } } return flag > heap.size(); } /* * (非 Javadoc) * * @see java.util.Collection#containsAll(java.util.Collection) */ @Override public boolean containsAll(Collection<?> c) { // TODO 自动生成的方法存根 return heap.containsAll(c); } /* * (非 Javadoc) * * @see java.util.Collection#addAll(java.util.Collection) */ @Override public boolean addAll(Collection<? extends T> c) { // TODO 自动生成的方法存根 for (T t : c) { insert(t); } return true; } /* * (非 Javadoc) * * @see java.util.Collection#removeAll(java.util.Collection) */ @Override public boolean removeAll(Collection<?> c) { // TODO 自动生成的方法存根 int flag = heap.size(); for (Object t : c) remove(t); return flag > heap.size(); } /* * (非 Javadoc) * * @see java.util.Collection#retainAll(java.util.Collection) */ @Override public boolean retainAll(Collection<?> c) { // TODO 自动生成的方法存根 int flag = heap.size(); for (int i = 0; i < heap.size(); i++) { if (!c.contains(heap.get(i))) { remove(heap.get(i)); i--; } } return flag > heap.size(); } /* * (非 Javadoc) * * @see java.util.Collection#clear() */ @Override public void clear() { // TODO 自动生成的方法存根 heap.clear(); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#size() */ @Override public int size() { // TODO 自动生成的方法存根 return heap.size(); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#peek() */ @Override public T peek() { // TODO 自动生成的方法存根 return heap.isEmpty() ? null : heap.get(0); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#element() */ @Override public T element() { // TODO 自动生成的方法存根 if (heap.isEmpty()) throw new NoSuchElementException(); return heap.get(0); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#remove() */ @Override public T remove() { if (heap.isEmpty()) throw new NoSuchElementException(); // TODO 自动生成的方法存根 return remove(0); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#poll() */ @Override public T poll() { // TODO 自动生成的方法存根 if (heap.isEmpty()) return null; return remove(0); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#insert(java.lang.Comparable) */ @Override public boolean insert(T t) { // TODO 自动生成的方法存根 heap.add(t); Up(heap.size()-1); return true; } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#add(java.lang.Comparable) */ @Override public boolean add(T t) { // TODO 自动生成的方法存根 return insert(t); } /** * 删除 * * @param index * @return */ protected T remove(int index) { T t = heap.get(index); heap.set(index, heap.get(heap.size()-1)); heap.remove(heap.size()-1); Down(index); return t; } /** * 上浮 * * @param index */ private void Up(int index) { while (index > 0) { int parent = index / 2; if (heap.get(parent).compareTo(heap.get(index)) >= 0) break; swap(index, parent); index = parent; } } /** * 下沉 * * @param index */ private void Down(int index) { while (2 * index <= this.size() - 1) { int child = 2 * index; if (child < this.size() - 1 && heap.get(child).compareTo(heap.get(child + 1)) < 0) { child++; } if (heap.get(index).compareTo(heap.get(child)) >= 0) { break; } swap(index, child); index = child; } } private void swap(int a, int b) { T temp = heap.get(a); heap.set(a, heap.get(b)); heap.set(b, temp); } public String toString(){ return heap.toString(); } public int hashCode(){ return heap.hashCode(); } @SuppressWarnings("rawtypes") public boolean equals(Object object){ if(object==this)return true; if(object==null) return false; if(this.getClass()!=object.getClass()) return false; return heap.equals(((MaxHeapImpl)object).heap); } }
最小堆:
/** * */ package com.lille.tool.heap; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; /** * 最小堆 * @author Lille qliujinming@qq.com * @time 2018年4月1日 下午11:50:03 */ public class MinHeapImpl<T extends Comparable<T>> implements Heap<T> { private ArrayList<T> heap; public MinHeapImpl() { heap=new ArrayList<T>(); } public MinHeapImpl(Collection<? extends T> c) { heap=new ArrayList<T>(c.size()+1); for (T t : c) insert(t); } /* * (非 Javadoc) * * @see java.util.Collection#isEmpty() */ @Override public boolean isEmpty() { // TODO 自动生成的方法存根 return heap.isEmpty(); } /* * (非 Javadoc) * * @see java.util.Collection#contains(java.lang.Object) */ @Override public boolean contains(Object o) { // TODO 自动生成的方法存根 return heap.contains(o); } /* * (非 Javadoc) * * @see java.util.Collection#iterator() */ @Override public Iterator<T> iterator() { // TODO 自动生成的方法存根 return new Itr(); } private class Itr implements Iterator<T> { int cursor = 0; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such /* * (非 Javadoc) * * @see java.util.Iterator#hasNext() */ @Override public boolean hasNext() { // TODO 自动生成的方法存根 return cursor < heap.size(); } /* * (非 Javadoc) * * @see java.util.Iterator#next() */ @Override public T next() { // TODO 自动生成的方法存根 lastRet = cursor; return heap.get(cursor++); } @Override public void remove() { if(lastRet<0)throw new RuntimeException("lastRet"+lastRet); MinHeapImpl.this.remove(lastRet); lastRet=-1; cursor--; } } /* * (非 Javadoc) * * @see java.util.Collection#toArray() */ @Override public Object[] toArray() { // TODO 自动生成的方法存根 return heap.toArray(); } /* * (非 Javadoc) * * @see java.util.Collection#toArray(java.lang.Object[]) */ @Override public <E> E[] toArray(E[] a) { // TODO 自动生成的方法存根 return heap.toArray(a); } /* * (非 Javadoc) * * @see java.util.Collection#remove(java.lang.Object) */ @Override public boolean remove(Object o) { // TODO 自动生成的方法存根 int flag = heap.size(); for (int i = 0; i < heap.size(); i++) { if (heap.get(i).equals(o)) { remove(i); i--; } } return flag > heap.size(); } /* * (非 Javadoc) * * @see java.util.Collection#containsAll(java.util.Collection) */ @Override public boolean containsAll(Collection<?> c) { // TODO 自动生成的方法存根 return heap.containsAll(c); } /* * (非 Javadoc) * * @see java.util.Collection#addAll(java.util.Collection) */ @Override public boolean addAll(Collection<? extends T> c) { // TODO 自动生成的方法存根 for (T t : c) { insert(t); } return true; } /* * (非 Javadoc) * * @see java.util.Collection#removeAll(java.util.Collection) */ @Override public boolean removeAll(Collection<?> c) { // TODO 自动生成的方法存根 int flag = heap.size(); for (Object t : c) remove(t); return flag > heap.size(); } /* * (非 Javadoc) * * @see java.util.Collection#retainAll(java.util.Collection) */ @Override public boolean retainAll(Collection<?> c) { // TODO 自动生成的方法存根 int flag = heap.size(); for (int i = 0; i < heap.size(); i++) { if (!c.contains(heap.get(i))) { remove(heap.get(i)); i--; } } return flag > heap.size(); } /* * (非 Javadoc) * * @see java.util.Collection#clear() */ @Override public void clear() { // TODO 自动生成的方法存根 heap.clear(); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#size() */ @Override public int size() { // TODO 自动生成的方法存根 return heap.size(); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#peek() */ @Override public T peek() { // TODO 自动生成的方法存根 return heap.isEmpty() ? null : heap.get(0); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#element() */ @Override public T element() { // TODO 自动生成的方法存根 if (heap.isEmpty()) throw new NoSuchElementException(); return heap.get(0); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#remove() */ @Override public T remove() { if (heap.isEmpty()) throw new NoSuchElementException(); // TODO 自动生成的方法存根 return remove(0); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#poll() */ @Override public T poll() { // TODO 自动生成的方法存根 if (heap.isEmpty()) return null; return remove(0); } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#insert(java.lang.Comparable) */ @Override public boolean insert(T t) { // TODO 自动生成的方法存根 heap.add(t); Up(heap.size()-1); return true; } /* * (非 Javadoc) * * @see com.lille.tool.heap.MaxHeap#add(java.lang.Comparable) */ @Override public boolean add(T t) { // TODO 自动生成的方法存根 return insert(t); } /** * 删除 * * @param index * @return */ protected T remove(int index) { T t = heap.get(index); heap.set(index, heap.get(heap.size()-1)); heap.remove(heap.size()-1); Down(index); return t; } /** * 上浮 * * @param index */ private void Up(int index) { while (index > 0) { int parent = index / 2; if (heap.get(parent).compareTo(heap.get(index)) <= 0) break; swap(index, parent); index = parent; } } /** * 下沉 * * @param index */ private void Down(int index) { while (2 * index <= this.size() - 1) { int child = 2 * index; if (child < this.size() - 1 && heap.get(child).compareTo(heap.get(child + 1)) > 0) { child++; } if (heap.get(index).compareTo(heap.get(child)) <= 0) { break; } swap(index, child); index = child; } } private void swap(int a, int b) { T temp = heap.get(a); heap.set(a, heap.get(b)); heap.set(b, temp); } public String toString(){ return heap.toString(); } public int hashCode(){ return heap.hashCode(); } @SuppressWarnings("rawtypes") public boolean equals(Object object){ if(object==this)return true; if(object==null) return false; if(this.getClass()!=object.getClass()) return false; return heap.equals(((MinHeapImpl)object).heap); } }
其实最大堆和最小堆的区别只是改了下沉和上浮里面的两个大于小于号,其它地方(包括注释)都是直接拷贝的。
测试用例:
1 @Test 2 public void test() { 3 Heap<Double> heap = new MinHeapImpl<Double>(); 4 double[] f = new double[] { 1.3, 3.4, 5.7, 5.4, 8.5, 9.3, 3.0, 4.4 }; 5 for (double d : f) 6 heap.insert(d); 7 System.out.println(heap); 8 Iterator<Double> it = heap.iterator(); 9 while (it.hasNext()) { 10 if (it.next() < 5) { 11 it.remove(); 12 System.out.println(heap); 13 } 14 } 15 }
写这个东西用了我四五个小时,因为时间有点晚了,明天还要上课,测试用例只准备了一组。运行正常,应当是没有问题的,不过最好还是准备几组数据自己测试一下。
转载请注明出处