18
votes

This question has been asked before in Stack Exchange but it went unanswered.

Link to the previously asked question: Binary Heap Implemented via a Binary Tree Structure

How do I implement heap in a binary tree. To implement heap, it is important to know the last filled node and the first unoccupied node. This could be done in level ordering of the tree, but then the time complexity will be O(n) just to find the first unoccupied node. So, how to implement heap in a binary tree in O(logn)?

Thanks Shekhar

8
It has been answered. What's wrong with the given answer? - nanofarad
The answer doesn't mention how to find first unoccupied leaf, it just mentions we need to add the new element in the first unoccupied leaf. To my understanding, you need to level order the tree to find the next unoccupied leaf and that will take O(n) - user2200660
As far as I can see you're basically keeping track of it by storing it. - nanofarad
Yes right, I tried to code it. The problem is, if you don't keep a pointer to the parent, then it's a problem to keep track of the next unoccupied leaf. We can maintain a variable to store this info, but calculating this will take O(n). Suppose we are in the 4th level (root is 0) and we have 4 elements starting from left in the 4th level. Now, to get next unoccupied leaf, we need to get the sibling of 2nd level, means go to 1st level parent. This takes O(n) because in a way we are doing level ordering. - user2200660

8 Answers

38
votes

To implement a heap with a binary tree with O(log n) time complexity, you need to store the total number of nodes as an instance variable.

Suppose we had a heap of 10 total nodes.

If we were to add a node...

We increment the total number of nodes by one. Now we have 11 total nodes. We convert the new total number of nodes (11) to its binary representation: 1011.

With the binary representation of the total nodes (1011), we get rid of the first digit. Afterwards, we use 011 to navigate through the tree to the next location to insert a node in. 0 means to go left and 1 means to go right. Therefore, with 011, we would go left, go right, and go right...which brings us to the next location to insert in.

We examined one node per level, making the time complexity O(log n)

7
votes

You won't implement the heap IN binary tree, because the heap is A binary tree. The heap maintains the following order property - given a node V, its parent is greater or equal to V. Also the heap is complete binary tree. I had ADS course at uni so I will give you my implementation of the heap in Java later in the answer. Just to list the main methods complexities that you obtain:

  • size() O(1)
  • isEmpty() O(1)
  • insert() O(logn)
  • removeMin() O(logn)
  • min() O(1)

Here is my Heap.java file:

public class Heap<E extends Comparable<E>> {

    private Object S[];
    private int last;
    private int capacity;

    public Heap() {
        S = new Object[11];
        last = 0;
        capacity = 7;
    }

    public Heap(int cap) {
        S = new Object[cap + 1];
        last = 0;
        capacity = cap;
    }

    public int size() {
        return last;
    }

    //
    // returns the number of elements in the heap
    //

    public boolean isEmpty() {
        return size() == 0;
    }

    //
    // is the heap empty?
    //

    public E min() throws HeapException {
        if (isEmpty())
            throw new HeapException("The heap is empty.");
        else
            return (E) S[1];
    }

    //
    // returns element with smallest key, without removal
    //

    private int compare(Object x, Object y) {
        return ((E) x).compareTo((E) y);
    }

    public void insert(E e) throws HeapException {
        if (size() == capacity)
            throw new HeapException("Heap overflow.");
        else{
            last++;
            S[last] = e;
            upHeapBubble();
        }       
    }

    // inserts e into the heap
    // throws exception if heap overflow
    //

    public E removeMin() throws HeapException {
        if (isEmpty())
            throw new HeapException("Heap is empty.");
        else {
            E min = min();
            S[1] = S[last];
            last--;
            downHeapBubble();
            return min;
        }
    }

    //
    // removes and returns smallest element of the heap
    // throws exception is heap is empty
    //

    /**
     * downHeapBubble() method is used after the removeMin() method to reorder the elements
     * in order to preserve the Heap properties
     */
    private void downHeapBubble(){
        int index = 1;
        while (true){
            int child = index*2;
            if (child > size())
                break;
            if (child + 1 <= size()){
                //if there are two children -> take the smalles or
                //if they are equal take the left one
                child = findMin(child, child + 1);
            }
            if (compare(S[index],S[child]) <= 0 )
                break;
            swap(index,child);
            index = child;
        }
    }

    /**
     * upHeapBubble() method is used after the insert(E e) method to reorder the elements
     * in order to preserve the Heap properties 
     */
    private void upHeapBubble(){
        int index = size();
        while (index > 1){
            int parent = index / 2;
            if (compare(S[index], S[parent]) >= 0)
                //break if the parent is greater or equal to the current element
                break;
            swap(index,parent);
            index = parent;
        }       
    }

    /**
     * Swaps two integers i and j
     * @param i
     * @param j
     */
    private void swap(int i, int j) {
        Object temp = S[i];
        S[i] = S[j];
        S[j] = temp;
    }

    /**
     * the method is used in the downHeapBubble() method
     * @param leftChild
     * @param rightChild
     * @return min of left and right child, if they are equal return the left
     */
    private int findMin(int leftChild, int rightChild) {
        if (compare(S[leftChild], S[rightChild]) <= 0)
            return leftChild;
        else
            return rightChild;
    }

    public String toString() {
        String s = "[";
        for (int i = 1; i <= size(); i++) {
            s += S[i];
            if (i != last)
                s += ",";
        }
        return s + "]";
    }
    //
    // outputs the entries in S in the order S[1] to S[last]
    // in same style as used in ArrayQueue
    //

}

HeapException.java:

public class HeapException extends RuntimeException {
    public HeapException(){};
    public HeapException(String msg){super(msg);}
}

The interesting part that gives you O(logn) performance is the downHeapBubble() and upHeapBubble() methods. I will add good explanation about them shortly.

upHeapBubble() is used when inserting new node to the heap. So when you insert you insert in the last position and then you need to call the upHeapBubble() like that:

last++;
S[last] = e;
upHeapBubble();

Then the last element is compared against it's parent and if the parent is greater - swap: this is done max logn times where n is the number of nodes. So here comes the logn performance.

For the deletion part - you can remove only min - the highest node. So when you remove it - you have to swap it with the last node - but then you have to maintain the heap property and you have to do a downHeapBubble(). If the node is greater than it's child swap with the smallest one and so on until you don't have any children left or you don't have smaller children. This can be done max logn times and so here comes the logn performance. You can explain yourself why this operation can be done max logn times by looking in the binary tree pictures here

5
votes

HEAP IMPLEMENTATION USING TREE

I am answering my own question that takes O(log n), but the limitation is to keep a pointer to the parent. if we don't keep a pointer to the parent, we need approximately O(n). I posted this question to get a solution for O(log n)

Here are the steps to calculate next unoccupied leaf (we have a pointer to the parent node):

x = last inserted node. We save this after every insertion.
y = tmp node
z = next unoccupied node (next insertion)
   if x is left child
      z = x -> parent -> rightchild (problem solved.. that was easy)
   else if x is right child
      go to x's parent, until parent becomes left child. Let this node be y
      (subtree rooted at y's sibling will contain the next unoccupied node)
      z = y -> parent -> right -> go left until null

This is O(log n), but needs a pointer to the parent.

O(n) solution would be pretty easy, just level order the tree and we get the location of the next unoccupied node.

My question is: how to locate next unoccupied node in O(log n) without using a parent pointer.

Thanks.

1
votes

Assuming you want to use a linked binary tree, with no pointers to parent nodes, then the only solution I can think of is keeping a counter of number of children in each node.

availableLeaf(node) {
    if( node.left is Empty || node.right is Empty )
        return node ;
    else
       if( node.left.count < node.right.count )
           return availableLeaf(node.left)
       else
           return availableLeaf(node.right)
}

This strategy also balances the number of nodes on each side of each subtree, which is beneficial (though extremely slightly).

This is O(log n). Keeping track of count on insertion requires to come all the way up to the roof, but this doesn't change the O(lon n) nature of this operation. Similar thing with deletion.

Other operations are the usual, and preserve their performance characteristics.

Do you need the details or prefer to work them out by yourself?

If you want to use a linked binary tree, with no other information than left and right pointers, then I'd suggest you to initiate a bounty for at least 100,000 points. I'm not saying it's impossible (because I don't have the math to prove it), but I'm saying that this has not been found in several decades (which I do know).

0
votes

My implementation of heap

public class Heap <T extends Comparable<T>> {
    private T[] arr;
    private int size;

    public Heap(T[] baseArr) {
        this.arr = baseArr;
        size = arr.length - 1;
    }

    public void minHeapify(int i, int n) {
        int l = 2 * i + 1;
        int r = 2 * i + 2;

        int smallest = i;
        if (l <= n && arr[l].compareTo(arr[smallest]) < 0) {
            smallest = l;
        }
        if (r <= n && arr[r].compareTo(arr[smallest]) < 0) {
            smallest = r;
        }

        if (smallest != i) {
            T temp = arr[i];
            arr[i] = arr[smallest];
            arr[smallest] = temp;
            minHeapify(smallest, n);
        }
    }

    public void buildMinHeap() {
        for (int i = size / 2; i >= 0; i--) {
            minHeapify(i, size);
        }
    }

    public void heapSortAscending() {
        buildMinHeap();
        int n = size;
        for (int i = n; i >= 1; i--) {
            T temp = arr[0];
            arr[0] = arr[i];
            arr[i] = temp;
            n--;
            minHeapify(0, n);
        }
    }
}
0
votes

The binary tree can be represented by an array:

import java.util.Arrays;

public class MyHeap {
    private Object[] heap;
    private int capacity;
    private int size;

    public MyHeap() {
        capacity = 8;
        heap = new Object[capacity];
        size = 0;
    }

    private void increaseCapacity() {
        capacity *= 2;
        heap = Arrays.copyOf(heap, capacity);
    }

    public int getSize() {
        return size;
    }

    public boolean isEmpty() {
        return size == 0;
    }

    public Object top() {
        return size > 0 ? heap[0] : null;
    }

    @SuppressWarnings("unchecked")
    public Object remove() {
        if (size == 0) {
            return null;
        }
        size--;
        Object res = heap[0];
        Object te = heap[size];
        int curr = 0, son = 1;
        while (son < size) {
            if (son + 1 < size
                    && ((Comparable<Object>) heap[son + 1])
                            .compareTo(heap[son]) < 0) {
                son++;
            }
            if (((Comparable<Object>) te).compareTo(heap[son]) <= 0) {
                break;
            }
            heap[curr] = heap[son];
            curr = son;
            son = 2 * curr + 1;
        }
        heap[curr] = te;
        return res;
    }

    @SuppressWarnings("unchecked")
    public void insert(Object e) {
        if (size == capacity) { // auto scaling
            increaseCapacity();
        }
        int curr = size;
        int parent;
        heap[size] = e;
        size++;
        while (curr > 0) {
            parent = (curr - 1) / 2;
            if (((Comparable<Object>) heap[parent]).compareTo(e) <= 0) {
                break;
            }
            heap[curr] = heap[parent];
            curr = parent;
        }
        heap[curr] = e;
    }
}

Usage:

    MyHeap heap = new MyHeap(); // it is a min heap
    heap.insert(18);
    heap.insert(26);
    heap.insert(35);
    System.out.println("size is " + heap.getSize() + ", top is " + heap.top());
    heap.insert(36);
    heap.insert(30);
    heap.insert(10);
    while(!heap.isEmpty()) {
        System.out.println(heap.remove());
    }
0
votes
import java.util.ArrayList;
import java.util.List;

/**
 * @author Harish R
 */
public class HeapPractise<T extends Comparable<T>> {

    private List<T> heapList;

    public List<T> getHeapList() {
        return heapList;
    }

    public void setHeapList(List<T> heapList) {
        this.heapList = heapList;
    }

    private int heapSize;

    public HeapPractise() {
        this.heapList = new ArrayList<>();
        this.heapSize = heapList.size();
    }

    public void insert(T item) {
        if (heapList.size() == 0) {
            heapList.add(item);
        } else {
            siftUp(item);
        }

    }

    private void siftUp(T item) {
        heapList.add(item);
        heapSize = heapList.size();
        int currentIndex = heapSize - 1;
        while (currentIndex > 0) {
            int parentIndex = (int) Math.floor((currentIndex - 1) / 2);
            T parentItem = heapList.get(parentIndex);
            if (parentItem != null) {
                if (item.compareTo(parentItem) > 0) {
                    heapList.set(parentIndex, item);
                    heapList.set(currentIndex, parentItem);
                    currentIndex = parentIndex;
                    continue;
                }
            }
            break;
        }
    }

    public T delete() {
        if (heapList.size() == 0) {
            return null;
        }
        if (heapList.size() == 1) {
            T item = heapList.get(0);
            heapList.remove(0);
            return item;
        }
        return siftDown();
    }

    private T siftDown() {
        T item = heapList.get(0);
        T lastItem = heapList.get(heapList.size() - 1);
        heapList.remove(heapList.size() - 1);
        heapList.set(0, lastItem);
        heapSize = heapList.size();
        int currentIndex = 0;
        while (currentIndex < heapSize) {
            int leftIndex = (2 * currentIndex) + 1;
            int rightIndex = (2 * currentIndex) + 2;
            T leftItem = null;
            T rightItem = null;
            int currentLargestItemIndex = -1;
            if (leftIndex <= heapSize - 1) {
                leftItem = heapList.get(leftIndex);
            }
            if (rightIndex <= heapSize - 1) {
                rightItem = heapList.get(rightIndex);
            }
            T currentLargestItem = null;
            if (leftItem != null && rightItem != null) {
                if (leftItem.compareTo(rightItem) >= 0) {
                    currentLargestItem = leftItem;
                    currentLargestItemIndex = leftIndex;
                } else {
                    currentLargestItem = rightItem;
                    currentLargestItemIndex = rightIndex;
                }
            } else if (leftItem != null && rightItem == null) {
                currentLargestItem = leftItem;
                currentLargestItemIndex = leftIndex;
            }
            if (currentLargestItem != null) {
                if (lastItem.compareTo(currentLargestItem) >= 0) {
                    break;
                } else {
                    heapList.set(currentLargestItemIndex, lastItem);
                    heapList.set(currentIndex, currentLargestItem);
                    currentIndex = currentLargestItemIndex;
                    continue;
                }
            } else {
                break;
            }
        }
        return item;

    }

    public static void main(String[] args) {
        HeapPractise<Integer> heap = new HeapPractise<>();

        for (int i = 0; i < 32; i++) {
            heap.insert(i);
        }
        System.out.println(heap.getHeapList());
        List<Node<Integer>> nodeArray = new ArrayList<>(heap.getHeapList()
                .size());
        for (int i = 0; i < heap.getHeapList().size(); i++) {
            Integer heapElement = heap.getHeapList().get(i);
            Node<Integer> node = new Node<Integer>(heapElement);
            nodeArray.add(node);
        }
        for (int i = 0; i < nodeArray.size(); i++) {
            int leftNodeIndex = (2 * i) + 1;
            int rightNodeIndex = (2 * i) + 2;
            Node<Integer> node = nodeArray.get(i);
            if (leftNodeIndex <= heap.getHeapList().size() - 1) {
                Node<Integer> leftNode = nodeArray.get(leftNodeIndex);
                node.left = leftNode;
            }
            if (rightNodeIndex <= heap.getHeapList().size() - 1) {
                Node<Integer> rightNode = nodeArray.get(rightNodeIndex);
                node.right = rightNode;
            }
        }
        BTreePrinter.printNode(nodeArray.get(0));
        System.out.println(heap.delete());
        nodeArray = new ArrayList<>(heap.getHeapList().size());
        for (int i = 0; i < heap.getHeapList().size(); i++) {
            Integer heapElement = heap.getHeapList().get(i);
            Node<Integer> node = new Node<Integer>(heapElement);
            nodeArray.add(node);
        }
        for (int i = 0; i < nodeArray.size(); i++) {
            int leftNodeIndex = (2 * i) + 1;
            int rightNodeIndex = (2 * i) + 2;
            Node<Integer> node = nodeArray.get(i);
            if (leftNodeIndex <= heap.getHeapList().size() - 1) {
                Node<Integer> leftNode = nodeArray.get(leftNodeIndex);
                node.left = leftNode;
            }
            if (rightNodeIndex <= heap.getHeapList().size() - 1) {
                Node<Integer> rightNode = nodeArray.get(rightNodeIndex);
                node.right = rightNode;
            }
        }
        BTreePrinter.printNode(nodeArray.get(0));
    }
}

public class Node<T extends Comparable<?>> {
    Node<T> left, right;
    T data;

    public Node(T data) {
        this.data = data;
    }
}

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

class BTreePrinter {

    public static <T extends Comparable<?>> void printNode(Node<T> root) {
        int maxLevel = BTreePrinter.maxLevel(root);

        printNodeInternal(Collections.singletonList(root), 1, maxLevel);
    }

    private static <T extends Comparable<?>> void printNodeInternal(
            List<Node<T>> nodes, int level, int maxLevel) {
        if (nodes.isEmpty() || BTreePrinter.isAllElementsNull(nodes))
            return;

        int floor = maxLevel - level;
        int endgeLines = (int) Math.pow(2, (Math.max(floor - 1, 0)));
        int firstSpaces = (int) Math.pow(2, (floor)) - 1;
        int betweenSpaces = (int) Math.pow(2, (floor + 1)) - 1;

        BTreePrinter.printWhitespaces(firstSpaces);

        List<Node<T>> newNodes = new ArrayList<Node<T>>();
        for (Node<T> node : nodes) {
            if (node != null) {
                String nodeData = String.valueOf(node.data);
                if (nodeData != null) {
                    if (nodeData.length() == 1) {
                        nodeData = "0" + nodeData;
                    }
                }
                System.out.print(nodeData);
                newNodes.add(node.left);
                newNodes.add(node.right);
            } else {
                newNodes.add(null);
                newNodes.add(null);
                System.out.print("  ");
            }

            BTreePrinter.printWhitespaces(betweenSpaces);
        }
        System.out.println("");

        for (int i = 1; i <= endgeLines; i++) {
            for (int j = 0; j < nodes.size(); j++) {
                BTreePrinter.printWhitespaces(firstSpaces - i);
                if (nodes.get(j) == null) {
                    BTreePrinter.printWhitespaces(endgeLines + endgeLines + i
                            + 1);
                    continue;
                }

                if (nodes.get(j).left != null)
                    System.out.print("//");
                else
                    BTreePrinter.printWhitespaces(1);

                BTreePrinter.printWhitespaces(i + i - 1);

                if (nodes.get(j).right != null)
                    System.out.print("\\\\");
                else
                    BTreePrinter.printWhitespaces(1);

                BTreePrinter.printWhitespaces(endgeLines + endgeLines - i);
            }

            System.out.println("");
        }

        printNodeInternal(newNodes, level + 1, maxLevel);
    }

    private static void printWhitespaces(int count) {
        for (int i = 0; i < 2 * count; i++)
            System.out.print(" ");
    }

    private static <T extends Comparable<?>> int maxLevel(Node<T> node) {
        if (node == null)
            return 0;

        return Math.max(BTreePrinter.maxLevel(node.left),
                BTreePrinter.maxLevel(node.right)) + 1;
    }

    private static <T> boolean isAllElementsNull(List<T> list) {
        for (Object object : list) {
            if (object != null)
                return false;
        }

        return true;
    }

}

Please note that BTreePrinter is a code I took somewhere in Stackoverflow long back and I modified to use with 2 digit numbers.It will be broken if we move to 3 digit numbers and it is only for simple understanding of how the Heap structure looks.A fix for 3 digit numbers is to keep everything as multiple of 3. Also due credits to Sesh Venugopal for wonderful tutorial on Youtube on Heap data structure

-2
votes

Here you go - Heap implementation using Binary Tree

public class PriorityQ<K extends Comparable<K>> {
private class TreeNode<T extends Comparable<T>> {
    T val;
    TreeNode<T> left, right, parent;
    public String toString() {
        return this.val.toString();
    }
    TreeNode(T v) {
        this.val = v;
        left = null;
        right = null;
    }
    public TreeNode<T> insert(T val, int position) {
        TreeNode<T> parent = findNode(position/2);
        TreeNode<T> node = new TreeNode<T>(val);
        if(position % 2 == 0) {
            parent.left = node;
        } else {
            parent.right = node;
        }
        node.parent = parent;
        heapify(node);
        return node;
    }
    private void heapify(TreeNode<T> node) {
        while(node.parent != null && (node.parent.val.compareTo(node.val) < 0)) {
            T temp = node.val;
            node.val = node.parent.val;
            node.parent.val = temp; 
            node = node.parent;
        }
    }       
    private TreeNode<T> findNode(int pos) {
        TreeNode<T> node = this;
        int reversed = 1;
        while(pos > 0) {
            reversed <<= 1;
            reversed |= (pos&1);                
            pos >>= 1;
        }
        reversed >>= 1;

        while(reversed > 1) {
            if((reversed & 1) == 0) {
                node = node.left;
            } else {
                node = node.right;
            }
            reversed >>= 1;
        }
        return node;
    }
    public TreeNode<T> remove(int pos) {
        if(pos <= 1) {
            return null;
        }
        TreeNode<T> last = findNode(pos);
        if(last.parent.right == last) {
            last.parent.right = null;
        } else {
            last.parent.left = null;
        }
        this.val = last.val;
        bubbleDown();
        return null;            
    }   

    public void bubbleDown() {
        TreeNode<T> node = this;            
        do {
            TreeNode<T> left = node.left;
            TreeNode<T> right = node.right;
            if(left != null && right != null) {
                T max = left.val.compareTo(right.val) > 0 ? left.val : right.val;
                if(max.compareTo(node.val) > 0) {
                    if(left.val.equals(max)) {
                        left.val = node.val;
                        node.val = max;                         
                        node = left;
                    } else {
                        right.val = node.val;
                        node.val = max;
                        node = right;
                    } 
                } else {
                    break;
                }
            } else if(left != null) {
                T max = left.val;
                if(left.val.compareTo(node.val) > 0) {
                    left.val = node.val;
                    node.val = max;                         
                    node = left;
                } else {
                    break;
                }
            } else {
                break;
            }
        } while(true);
    }
}
private TreeNode<K> root;
private int position;
PriorityQ(){ 
    this.position = 1;
}
public void insert(K val) {
    if(val == null) {
        return;
    }
    if(root == null) {
        this.position = 1;
        root = new TreeNode<K>(val);
        this.position++;
        return ;
    }
    root.insert(val, position);
    position++;     
}
public K remove() {
    if(root == null) {
        return null;
    }
    K val = root.val;
    root.remove(this.position-1);
    this.position--;
    if(position == 1) {
        root = null;
    }
    return val;
}
public static void main(String[] args) {
    PriorityQ<Integer> q = new PriorityQ<>();
    System.out.println(q.remove());
    q.insert(1);
    q.insert(11);
    q.insert(111);
    q.insert(1111);
    q.remove();
    q.remove();
    q.remove();
    q.remove();
    q.insert(2);
    q.insert(4);
}

}