package com.jwetherell.algorithms.data_structures; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Deque; import java.util.List; import com.jwetherell.algorithms.data_structures.interfaces.IHeap; /** * A binary heap is a heap data structure created using a binary tree. It can be * seen as a binary tree with two additional constraints: 1) The shape property: * the tree is a complete binary tree; that is, all levels of the tree, except * possibly the last one (deepest) are fully filled, and, if the last level of * the tree is not complete, the nodes of that level are filled from left to * right. 2) The heap property: each node is right than or equal to each of its * children according to a comparison predicate defined for the data structure. * * http://en.wikipedia.org/wiki/Binary_heap * * @author Justin Wetherell <phishman3579@gmail.com> */ @SuppressWarnings("unchecked") public interface BinaryHeap<T extends Comparable<T>> extends IHeap<T> { public enum HeapType { Tree, Array } public enum Type { MIN, MAX } /** * Get the heap in array form. * * @return array representing the heap. */ public T[] getHeap(); /** * A binary heap using an array to hold the nodes. * * @author Justin Wetherell <phishman3579@gmail.com> */ public static class BinaryHeapArray<T extends Comparable<T>> implements BinaryHeap<T> { private static final int MINIMUM_SIZE = 1024; private Type type = Type.MIN; private int size = 0; private T[] array = (T[]) new Comparable[MINIMUM_SIZE]; /** * Get the parent index of this index, will return Integer.MIN_VALUE if * no parent is possible. * * @param index * of the node to find a parent for. * @return index of parent node or Integer.MIN_VALUE if no parent. */ private static final int getParentIndex(int index) { if (index > 0) return (int) Math.floor((index - 1) / 2); return Integer.MIN_VALUE; } /** * Get the left child index of this index. * * @param index * of the node to find a left child for. * @return index of left child node. */ private static final int getLeftIndex(int index) { return 2 * index + 1; } /** * Get the right child index of this index. * * @param index * of the node to find a right child for. * @return index of right child node. */ private static final int getRightIndex(int index) { return 2 * index + 2; } /** * Constructor for heap, defaults to a min-heap. */ public BinaryHeapArray() { size = 0; } /** * Constructor for heap. * * @param type * Heap type. */ public BinaryHeapArray(Type type) { this(); this.type = type; } /** * {@inheritDoc} */ @Override public int size() { return size; } /** * {@inheritDoc} */ @Override public boolean add(T value) { if (size >= array.length) grow(); array[size] = value; heapUp(size++); return true; } /** * {@inheritDoc} */ @Override public T remove(T value) { if (array.length == 0) return null; for (int i = 0; i < size; i++) { T node = array[i]; if (node.equals(value)) return remove(i); } return null; } private T remove(int index) { if (index<0 || index>=size) return null; T t = array[index]; array[index] = array[--size]; array[size] = null; heapDown(index); int shrinkSize = array.length>>1; if (shrinkSize >= MINIMUM_SIZE && size < shrinkSize) shrink(); return t; } protected void heapUp(int idx) { int nodeIndex = idx; T value = this.array[nodeIndex]; if (value==null) return; while (nodeIndex >= 0) { int parentIndex = getParentIndex(nodeIndex); if (parentIndex < 0) return; T parent = this.array[parentIndex]; if ((type == Type.MIN && value.compareTo(parent) < 0) || (type == Type.MAX && value.compareTo(parent) > 0) ) { // Node is greater/lesser than parent, switch node with parent this.array[parentIndex] = value; this.array[nodeIndex] = parent; } else { return; } nodeIndex = parentIndex; } } protected void heapDown(int index) { T value = this.array[index]; if (value==null) return; int leftIndex = getLeftIndex(index); int rightIndex = getRightIndex(index); T left = (leftIndex != Integer.MIN_VALUE && leftIndex < this.size) ? this.array[leftIndex] : null; T right = (rightIndex != Integer.MIN_VALUE && rightIndex < this.size) ? this.array[rightIndex] : null; if (left == null && right == null) { // Nothing to do here return; } T nodeToMove = null; int nodeToMoveIndex = -1; if ((type == Type.MIN && left != null && right != null && value.compareTo(left) > 0 && value.compareTo(right) > 0) || (type == Type.MAX && left != null && right != null && value.compareTo(left) < 0 && value.compareTo(right) < 0)) { // Both children are greater/lesser than node if ((right!=null) && ((type == Type.MIN && (right.compareTo(left) < 0)) || ((type == Type.MAX && right.compareTo(left) > 0))) ) { // Right is greater/lesser than left nodeToMove = right; nodeToMoveIndex = rightIndex; } else if ((left!=null) && ((type == Type.MIN && left.compareTo(right) < 0) || (type == Type.MAX && left.compareTo(right) > 0)) ) { // Left is greater/lesser than right nodeToMove = left; nodeToMoveIndex = leftIndex; } else { // Both children are equal, use right nodeToMove = right; nodeToMoveIndex = rightIndex; } } else if ((type == Type.MIN && right != null && value.compareTo(right) > 0) || (type == Type.MAX && right != null && value.compareTo(right) < 0) ) { // Right is greater/lesser than node nodeToMove = right; nodeToMoveIndex = rightIndex; } else if ((type == Type.MIN && left != null && value.compareTo(left) > 0) || (type == Type.MAX && left != null && value.compareTo(left) < 0) ) { // Left is greater/lesser than node nodeToMove = left; nodeToMoveIndex = leftIndex; } // No node to move, stop recursion if (nodeToMove == null) return; // Re-factor heap sub-tree this.array[nodeToMoveIndex] = value; this.array[index] = nodeToMove; heapDown(nodeToMoveIndex); } // Grow the array by 50% private void grow() { int growSize = size + (size<<1); array = Arrays.copyOf(array, growSize); } // Shrink the array by 50% private void shrink() { int shrinkSize = array.length>>1; array = Arrays.copyOf(array, shrinkSize); } /** * {@inheritDoc} */ @Override public void clear() { size = 0; } /** * {@inheritDoc} */ @Override public boolean contains(T value) { if (array.length == 0) return false; for (int i = 0; i < size; i++) { T t = array[i]; if (t.equals(value)) return true; } return false; } /** * {@inheritDoc} */ @Override public boolean validate() { if (array.length == 0) return true; return validateNode(0); } /** * Validate the node for the heap invariants. * * @param node * to validate for. * @return True if node is valid. */ private boolean validateNode(int index) { T value = this.array[index]; int leftIndex = getLeftIndex(index); int rightIndex = getRightIndex(index); // We shouldn't ever have a right node without a left in a heap if (rightIndex != Integer.MIN_VALUE && leftIndex == Integer.MIN_VALUE) return false; if (leftIndex != Integer.MIN_VALUE && leftIndex < size) { T left = this.array[leftIndex]; if ((type == Type.MIN && value.compareTo(left) < 0) || (type == Type.MAX && value.compareTo(left) > 0)) { return validateNode(leftIndex); } return false; } if (rightIndex != Integer.MIN_VALUE && rightIndex < size) { T right = this.array[rightIndex]; if ((type == Type.MIN && value.compareTo(right) < 0) || (type == Type.MAX && value.compareTo(right) > 0)) { return validateNode(rightIndex); } return false; } return true; } /** * {@inheritDoc} */ @Override public T[] getHeap() { T[] nodes = (T[]) new Comparable[size]; if (array.length == 0) return nodes; for (int i = 0; i < size; i++) { T node = this.array[i]; nodes[i] = node; } return nodes; } /** * {@inheritDoc} */ @Override public T getHeadValue() { if (array.length == 0) return null; return array[0]; } /** * {@inheritDoc} */ @Override public T removeHead() { return remove(getHeadValue()); } /** * {@inheritDoc} */ @Override public java.util.Collection<T> toCollection() { return (new JavaCompatibleBinaryHeapArray<T>(this)); } /** * {@inheritDoc} */ @Override public String toString() { return HeapPrinter.getString(this); } protected static class HeapPrinter { public static <T extends Comparable<T>> String getString(BinaryHeapArray<T> tree) { if (tree.array.length == 0) return "Tree has no nodes."; T root = tree.array[0]; if (root == null) return "Tree has no nodes."; return getString(tree, 0, "", true); } private static <T extends Comparable<T>> String getString(BinaryHeapArray<T> tree, int index, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); T value = tree.array[index]; builder.append(prefix + (isTail ? "└── " : "├── ") + value + "\n"); List<Integer> children = null; int leftIndex = getLeftIndex(index); int rightIndex = getRightIndex(index); if (leftIndex != Integer.MIN_VALUE || rightIndex != Integer.MIN_VALUE) { children = new ArrayList<Integer>(2); if (leftIndex != Integer.MIN_VALUE && leftIndex < tree.size) { children.add(leftIndex); } if (rightIndex != Integer.MIN_VALUE && rightIndex < tree.size) { children.add(rightIndex); } } if (children != null) { for (int i = 0; i < children.size() - 1; i++) { builder.append(getString(tree, children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() >= 1) { builder.append(getString(tree, children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } return builder.toString(); } } } public static class BinaryHeapTree<T extends Comparable<T>> implements BinaryHeap<T> { private Type type = Type.MIN; private int size = 0; private Node<T> root = null; /** * Constructor for heap, defaults to a min-heap. */ public BinaryHeapTree() { root = null; size = 0; } /** * Constructor for heap. * * @param type * Heap type. */ public BinaryHeapTree(Type type) { this(); this.type = type; } /** * {@inheritDoc} */ @Override public int size() { return size; } /** * Get the navigation directions through the tree to the index. * * @param index * of the Node to get directions for. * @return Integer array representing the directions to the index. */ private static int[] getDirections(int idx) { int index = idx; int directionsSize = (int) (Math.log10(index + 1) / Math.log10(2)) - 1; int[] directions = null; if (directionsSize > 0) { directions = new int[directionsSize]; int i = directionsSize - 1; while (i >= 0) { index = (index - 1) / 2; directions[i--] = (index > 0 && index % 2 == 0) ? 1 : 0; // 0=left, 1=right } } return directions; } /** * {@inheritDoc} */ @Override public boolean add(T value) { return add(new Node<T>(null, value)); } private boolean add(Node<T> newNode) { if (root == null) { root = newNode; size++; return true; } Node<T> node = root; int[] directions = getDirections(size); // size == index of new node if (directions != null && directions.length > 0) { for (int d : directions) { if (d == 0) { // Go left node = node.left; } else { // Go right node = node.right; } } } if (node.left == null) { node.left = newNode; } else { node.right = newNode; } newNode.parent = node; size++; heapUp(newNode); return true; } /** * Remove the root node. */ private void removeRoot() { replaceNode(root); } private Node<T> getLastNode() { // Find the last node int[] directions = getDirections(size-1); // Directions to the last node Node<T> lastNode = root; if (directions != null && directions.length > 0) { for (int d : directions) { if (d == 0) { // Go left lastNode = lastNode.left; } else { // Go right lastNode = lastNode.right; } } } if (lastNode.right != null) { lastNode = lastNode.right; } else if (lastNode.left != null) { lastNode = lastNode.left; } return lastNode; } /** * Replace the node with the last node and heap down. * * @param node to replace. */ private void replaceNode(Node<T> node) { Node<T> lastNode = getLastNode(); // Remove lastNode from tree Node<T> lastNodeParent = lastNode.parent; if (lastNodeParent!=null) { if (lastNodeParent.right != null) { lastNodeParent.right = null; } else { lastNodeParent.left = null; } lastNode.parent = null; } if (node.parent!=null) { if (node.parent.left.equals(node)) { node.parent.left = lastNode; } else { node.parent.right = lastNode; } } lastNode.parent = node.parent; lastNode.left = node.left; if (node.left!=null) node.left.parent = lastNode; lastNode.right = node.right; if (node.right!=null) node.right.parent = lastNode; if (node.equals(root)) { if (!lastNode.equals(root)) root = lastNode; else root = null; } size--; // Last node is the node to remove if (lastNode.equals(node)) return; if (lastNode.equals(root)) { heapDown(lastNode); } else { heapDown(lastNode); heapUp(lastNode); } } /** * Get the node in the startingNode sub-tree which has the value. * * @param startingNode * node rooted sub-tree to search in. * @param value * to search for. * @return Node<T> which equals value in sub-tree or NULL if not found. */ private Node<T> getNode(Node<T> startingNode, T value) { Node<T> result = null; if (startingNode != null && startingNode.value.equals(value)) { result = startingNode; } else if (startingNode != null && !startingNode.value.equals(value)) { Node<T> left = startingNode.left; Node<T> right = startingNode.right; if (left != null && ((type==Type.MIN && left.value.compareTo(value)<=0)||(type==Type.MAX && left.value.compareTo(value)>=0)) ) { result = getNode(left, value); if (result != null) return result; } if (right != null && ((type==Type.MIN && right.value.compareTo(value)<=0)||(type==Type.MAX && right.value.compareTo(value)>=0)) ) { result = getNode(right, value); if (result != null) return result; } } return result; } /** * {@inheritDoc} */ @Override public void clear() { root = null; size = 0; } /** * {@inheritDoc} */ @Override public boolean contains(T value) { if (root == null) return false; Node<T> node = getNode(root,value); return (node != null); } /** * {@inheritDoc} */ @Override public T remove(T value) { if (root == null) return null; Node<T> node = getNode(root,value); if (node!=null) { T t = node.value; replaceNode(node); return t; } return null; } /** * Heap up the heap from this node. * * @param nodeToHeapUp * to heap up. */ protected void heapUp(Node<T> nodeToHeapUp) { Node<T> node = nodeToHeapUp; while (node != null) { Node<T> heapNode = node; Node<T> parent = heapNode.parent; if ((parent != null) && ((type == Type.MIN && node.value.compareTo(parent.value) < 0) || (type == Type.MAX && node.value.compareTo(parent.value) > 0)) ) { // Node is less than parent, switch node with parent Node<T> grandParent = parent.parent; Node<T> parentLeft = parent.left; Node<T> parentRight = parent.right; parent.left = heapNode.left; if (parent.left != null) parent.left.parent = parent; parent.right = heapNode.right; if (parent.right != null) parent.right.parent = parent; if (parentLeft != null && parentLeft.equals(node)) { heapNode.left = parent; heapNode.right = parentRight; if (parentRight != null) parentRight.parent = heapNode; } else { heapNode.right = parent; heapNode.left = parentLeft; if (parentLeft != null) parentLeft.parent = heapNode; } parent.parent = heapNode; if (grandParent == null) { // New root. heapNode.parent = null; root = heapNode; } else { Node<T> grandLeft = grandParent.left; if (grandLeft != null && grandLeft.equals(parent)) { grandParent.left = heapNode; } else { grandParent.right = heapNode; } heapNode.parent = grandParent; } } else { node = heapNode.parent; } } } /** * Heap down the heap from this node. * * @param nodeToHeapDown * to heap down. */ protected void heapDown(Node<T> nodeToHeapDown) { if (nodeToHeapDown==null) return; Node<T> node = nodeToHeapDown; Node<T> heapNode = node; Node<T> left = heapNode.left; Node<T> right = heapNode.right; if (left == null && right == null) { // Nothing to do here return; } Node<T> nodeToMove = null; if ((left != null && right != null ) && ((type == Type.MIN && node.value.compareTo(left.value) > 0 && node.value.compareTo(right.value) > 0) || (type == Type.MAX && node.value.compareTo(left.value) < 0 && node.value.compareTo(right.value) < 0)) ) { // Both children are greater/lesser than node if ((type == Type.MIN && right.value.compareTo(left.value) < 0) || (type == Type.MAX && right.value.compareTo(left.value) > 0)) { // Right is greater/lesser than left nodeToMove = right; } else if ((type == Type.MIN && left.value.compareTo(right.value) < 0) || (type == Type.MAX && left.value.compareTo(right.value) > 0)) { // Left is greater/lesser than right nodeToMove = left; } else { // Both children are equal, use right nodeToMove = right; } } else if ((type == Type.MIN && right != null && node.value.compareTo(right.value) > 0) || (type == Type.MAX && right != null && node.value.compareTo(right.value) < 0)) { // Right is greater than node nodeToMove = right; } else if ((type == Type.MIN && left != null && node.value.compareTo(left.value) > 0) || (type == Type.MAX && left != null && node.value.compareTo(left.value) < 0)) { // Left is greater than node nodeToMove = left; } // No node to move, stop recursion if (nodeToMove == null) return; // Re-factor heap sub-tree Node<T> nodeParent = heapNode.parent; if (nodeParent == null) { // heap down the root root = nodeToMove; root.parent = null; } else { if (nodeParent.left!=null && nodeParent.left.equals(node)) { // heap down a left nodeParent.left = nodeToMove; nodeToMove.parent = nodeParent; } else { // heap down a right nodeParent.right = nodeToMove; nodeToMove.parent = nodeParent; } } Node<T> nodeLeft = heapNode.left; Node<T> nodeRight = heapNode.right; Node<T> nodeToMoveLeft = nodeToMove.left; Node<T> nodeToMoveRight = nodeToMove.right; if (nodeLeft!=null && nodeLeft.equals(nodeToMove)) { nodeToMove.right = nodeRight; if (nodeRight != null) nodeRight.parent = nodeToMove; nodeToMove.left = heapNode; } else { nodeToMove.left = nodeLeft; if (nodeLeft != null) nodeLeft.parent = nodeToMove; nodeToMove.right = heapNode; } heapNode.parent = nodeToMove; heapNode.left = nodeToMoveLeft; if (nodeToMoveLeft != null) nodeToMoveLeft.parent = heapNode; heapNode.right = nodeToMoveRight; if (nodeToMoveRight != null) nodeToMoveRight.parent = heapNode; heapDown(node); } /** * {@inheritDoc} */ @Override public boolean validate() { if (root == null) return true; return validateNode(root); } /** * Validate node for heap invariants. * * @param node * to validate for. * @return True if node is valid. */ private boolean validateNode(Node<T> node) { Node<T> left = node.left; Node<T> right = node.right; // We shouldn't ever have a right node without a left in a heap if (right != null && left == null) return false; if (left != null) { if ((type == Type.MIN && node.value.compareTo(left.value) < 0) || (type == Type.MAX && node.value.compareTo(left.value) > 0)) { return validateNode(left); } return false; } if (right != null) { if ((type == Type.MIN && node.value.compareTo(right.value) < 0) || (type == Type.MAX && node.value.compareTo(right.value) > 0)) { return validateNode(right); } return false; } return true; } /** * Populate the node in the array at the index. * * @param node * to populate. * @param idx * of node in array. * @param array * where the node lives. */ private void getNodeValue(Node<T> node, int idx, T[] array) { int index = idx; array[index] = node.value; index = (index * 2) + 1; Node<T> left = node.left; if (left != null) getNodeValue(left, index, array); Node<T> right = node.right; if (right != null) getNodeValue(right, index + 1, array); } /** * {@inheritDoc} */ @Override public T[] getHeap() { T[] nodes = (T[]) new Comparable[size]; if (root != null) getNodeValue(root, 0, nodes); return nodes; } /** * {@inheritDoc} */ @Override public T getHeadValue() { T result = null; if (root != null) result = root.value; return result; } /** * {@inheritDoc} */ @Override public T removeHead() { T result = null; if (root != null) { result = root.value; removeRoot(); } return result; } /** * {@inheritDoc} */ @Override public java.util.Collection<T> toCollection() { return (new JavaCompatibleBinaryHeapTree<T>(this)); } /** * {@inheritDoc} */ @Override public String toString() { return HeapPrinter.getString(this); } protected static class HeapPrinter { public static <T extends Comparable<T>> void print(BinaryHeapTree<T> tree) { System.out.println(getString(tree.root, "", true)); } public static <T extends Comparable<T>> String getString(BinaryHeapTree<T> tree) { if (tree.root == null) return "Tree has no nodes."; return getString(tree.root, "", true); } private static <T extends Comparable<T>> String getString(Node<T> node, String prefix, boolean isTail) { StringBuilder builder = new StringBuilder(); builder.append(prefix + (isTail ? "└── " : "├── ") + node.value + "\n"); List<Node<T>> children = null; if (node.left != null || node.right != null) { children = new ArrayList<Node<T>>(2); if (node.left != null) children.add(node.left); if (node.right != null) children.add(node.right); } if (children != null) { for (int i = 0; i < children.size() - 1; i++) { builder.append(getString(children.get(i), prefix + (isTail ? " " : "│ "), false)); } if (children.size() >= 1) { builder.append(getString(children.get(children.size() - 1), prefix + (isTail ? " " : "│ "), true)); } } return builder.toString(); } } private static class Node<T extends Comparable<T>> { private T value = null; private Node<T> parent = null; private Node<T> left = null; private Node<T> right = null; private Node(Node<T> parent, T value) { this.value = value; this.parent = parent; } /** * {@inheritDoc} */ @Override public String toString() { return "value=" + value + " parent=" + ((parent != null) ? parent.value : "NULL") + " left=" + ((left != null) ? left.value : "NULL") + " right=" + ((right != null) ? right.value : "NULL"); } } } public static class JavaCompatibleBinaryHeapArray<T extends Comparable<T>> extends java.util.AbstractCollection<T> { private BinaryHeapArray<T> heap = null; public JavaCompatibleBinaryHeapArray() { heap = new BinaryHeapArray<T>(); } public JavaCompatibleBinaryHeapArray(BinaryHeapArray<T> heap) { this.heap = heap; } /** * {@inheritDoc} */ @Override public boolean add(T value) { return heap.add(value); } /** * {@inheritDoc} */ @Override public boolean remove(Object value) { return (heap.remove((T)value)!=null); } /** * {@inheritDoc} */ @Override public boolean contains(Object value) { return heap.contains((T)value); } /** * {@inheritDoc} */ @Override public int size() { return heap.size(); } /** * {@inheritDoc} */ @Override public java.util.Iterator<T> iterator() { return (new BinaryHeapArrayIterator<T>(this.heap)); } private static class BinaryHeapArrayIterator<T extends Comparable<T>> implements java.util.Iterator<T> { private BinaryHeapArray<T> heap = null; private int last = -1; private int index = -1; protected BinaryHeapArrayIterator(BinaryHeapArray<T> heap) { this.heap = heap; } /** * {@inheritDoc} */ @Override public boolean hasNext() { if (index+1>=heap.size) return false; return (heap.array[index+1]!=null); } /** * {@inheritDoc} */ @Override public T next() { if (++index>=heap.size) return null; last = index; return heap.array[index]; } /** * {@inheritDoc} */ @Override public void remove() { heap.remove(last); } } } public static class JavaCompatibleBinaryHeapTree<T extends Comparable<T>> extends java.util.AbstractCollection<T> { private BinaryHeapTree<T> heap = null; public JavaCompatibleBinaryHeapTree() { heap = new BinaryHeapTree<T>(); } public JavaCompatibleBinaryHeapTree(BinaryHeapTree<T> heap) { this.heap = heap; } /** * {@inheritDoc} */ @Override public boolean add(T value) { return heap.add(value); } /** * {@inheritDoc} */ @Override public boolean remove(Object value) { return (heap.remove((T)value)!=null); } /** * {@inheritDoc} */ @Override public boolean contains(Object value) { return heap.contains((T)value); } /** * {@inheritDoc} */ @Override public int size() { return heap.size(); } /** * {@inheritDoc} */ @Override public java.util.Iterator<T> iterator() { return (new BinaryHeapTreeIterator<T>(this.heap)); } private static class BinaryHeapTreeIterator<C extends Comparable<C>> implements java.util.Iterator<C> { private BinaryHeapTree<C> heap = null; private BinaryHeapTree.Node<C> last = null; private Deque<BinaryHeapTree.Node<C>> toVisit = new ArrayDeque<BinaryHeapTree.Node<C>>(); protected BinaryHeapTreeIterator(BinaryHeapTree<C> heap) { this.heap = heap; if (heap.root!=null) toVisit.add(heap.root); } /** * {@inheritDoc} */ @Override public boolean hasNext() { if (toVisit.size()>0) return true; return false; } /** * {@inheritDoc} */ @Override public C next() { while (toVisit.size()>0) { // Go thru the current nodes BinaryHeapTree.Node<C> n = toVisit.pop(); // Add non-null children if (n.left!=null) toVisit.add(n.left); if (n.right!=null) toVisit.add(n.right); // Update last node (used in remove method) last = n; return n.value; } return null; } /** * {@inheritDoc} */ @Override public void remove() { heap.replaceNode(last); } } } }