package com.company.binaryHeapTree; /** * 堆的基本API * * @param <E> */ public interface Heap<E> { int size(); // 元素的数量 boolean isEmpty(); // 是否为空 void clear(); // 清空 void add(E element); // 添加元素 E get(); // 获得堆顶元素 E remove(); // 删除堆顶元素 E replace(E element); // 删除堆顶元素的同时插入一个新元素 }
package com.company.binaryHeapTree; import java.util.Arrays; import java.util.Comparator; /** * 设计背景: * 现在需要一种数据结构要满足如下条件: * 1、添加元素 * 2、获取最大值 * 3、删除最大值 * 实现方案: * 1、动态数组/双向链表 时间复杂度分别为 O(n) O(n) O(n) * 2、有序动态数组、双向链表 时间复杂度分别为 O(1) O(1) O(1) * 3、BBST平衡二叉搜索树 时间复杂度分别为 O(logn) O(logn) O(logn) * 然而以上三种方式要么时间复杂度太大 要么全排序浪费性能 要么实现过于复杂 * 因此需要思考一种更加合理的数据结构 ,那就是二叉堆 获取最大值 O(1) 添加元素O(logn) 删除元素O(logn) * 相关应用: * 1、TOP K 问题,从海量数据中找出前K个元素 * 2、优先级队列 * <p> * 堆的一个重要性质,任意节点的值总是 >= 或者 <= 子节点的值 * 二叉堆的逻辑结构就是一棵完全二叉树,鉴于完全二叉树的一些特性,二叉堆底层可以用数组实现 * <p> * 索引i的规律(n是元素数量) * 1、如果i = 0,那么它是根节点 * 2、如果i > 0,那么它的父节点是floor((i-1)/2) * 3、如果2i + 1 <= n-1 那么他的做子节点的索引为 2i + 1 * 4、如果2i + 1 > n-1 那么它无左子节点 * 5、如果2i + 2 <= n-1 那么它的右子节点的索引为 2i + 2 * 6、如果2i + 2 > n-1 那么它无右子节点 */ public class BinaryHeapTree<E> implements Heap { private E[] elements; private static final int DEFAULT_CAPACITY = 10; private int size; protected Comparator<E> comparator; public BinaryHeapTree(Comparator<E> comparator) { this.elements = (E[]) new Object[DEFAULT_CAPACITY]; this.comparator = comparator; } public int compare(E e1, E e2) { return comparator != null ? comparator.compare(e1, e2) : ((Comparable<E>) e1).compareTo(e2); } @Override public int size() { return size; } @Override public boolean isEmpty() { return size == 0; } private void emptyCheck() { if (isEmpty()) { throw new IndexOutOfBoundsException("Heap is empty"); } } @Override public void clear() { for (int i = 0; i < size; i++) { elements[i] = null; } size = 0; } @Override public void add(Object element) { elementNotNullCheck((E) element); dilatation(size + 1); elements[size++] = (E) element; siftUp(size - 1); } private void elementNotNullCheck(E e) { if (e == null) { throw new IllegalArgumentException("Element cannot be null"); } } /** * 往堆中添加元素 不断地与父元素相比较 直到小于等于父元素 * * @param index */ private void siftUp(int index) { E element = elements[index]; while (index > 0) { int parentIndex = (index - 1) >> 1; E parent = elements[parentIndex]; if (compare(parent, element) > 0) break; elements[index] = parent; index = parentIndex; } elements[index] = element; } private void dilatation(int index) { int oldCapacity = elements.length; if (index == oldCapacity) { System.out.println("数组要扩容了"); int newCapacity = oldCapacity + (oldCapacity >> 1); elements = Arrays.copyOf(elements, newCapacity); } } @Override public Object get() { emptyCheck(); return elements[0]; } @Override public Object remove() { emptyCheck(); E top = elements[0]; int lastIndex = --size; elements[0] = elements[lastIndex]; elements[lastIndex] = null; siftDown(0); return top; } private void siftDown(int index) { E last = elements[index]; int half = size >> 1; while (index < half) {//index必须是非叶子节点 int childIndex = (index << 1) + 1; E child = elements[childIndex]; int rightIndex = childIndex + 1; if (rightIndex < size && compare(child, elements[rightIndex]) < 0) { child = elements[childIndex = rightIndex]; } if (compare(last, child) >= 0) break; elements[index] = child; index = childIndex; } elements[index] = last; } @Override public Object replace(Object element) { return null; } }
package com.company.binaryHeapTree; import java.util.Comparator; public class Main { public static void main(String[] args) { BinaryHeapTree<Integer> heap = new BinaryHeapTree<>(new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { return o1.compareTo(o2); } }); heap.add(7); heap.add(9); heap.add(45); heap.add(7); heap.add(5); heap.add(1); heap.add(0); heap.add(2); heap.add(66); heap.add(4); while (!heap.isEmpty()) { System.out.println(heap.remove()); } } }