Data Structures and Algorithms in Java
GitHubAuthor
  • Preface
    • Syllabus
    • Schedule
  • 0. Getting Started
    • 0.1. Environment Setup
    • 0.2. Quiz
  • 1. Java Essentials
    • 1.1. Abstraction
    • 1.2. Implementation
    • 1.3. Unit Testing
    • 1.4. Quiz
  • 2. Priority Queues
    • 2.1. Simple Priority Queues
    • 2.2. Binary Heap
    • 2.3. Unit Testing
    • 2.4. Benchmarking
    • 2.5. Quiz
  • 3. Sorting Algorithms
    • 3.1. Abstraction
    • 3.2. Comparison-based Sort
    • 3.3. Divide & Conquer Sort
    • 3.4. Distribution-based Sort
    • 3.5. Quiz
    • 3.6. Homework
  • 4. Binary Search Trees
    • 4.1. Binary Search Trees
    • 4.2. Balanced BST
    • 4.2. AVL Trees
    • 4.3. Red-Black Trees
    • 4.4. Quiz
  • 5. Tries
    • 5.1. Concept
    • 5.2. Implementation
    • 5.3. Quiz
    • 5.4. Homework
  • 6. Disjoint Sets
    • 6.1. Concept
    • 6.2. Implementation
    • 6.3. Quiz
  • 7. Graphs
    • 7.1. Implementation
    • 7.2. Cycle Detection
    • 7.3. Topological Sorting
    • 7.4. Quiz
  • 8. Minimum Spanning Trees
    • 8.1. Abstraction
    • 8.2. Prim's Algorithm
    • 8.3. Kruskal’s Algorithm
    • 8.4. Edmonds' Algorithm
    • 8.5. Quiz
    • 8.6. Homework
  • 9. Network Flow
    • 9.1. Flow Network
    • 9.2. Ford-Fulkerson Algorithm
    • 9.3. Simplex Algorithm
    • 9.3. Quiz
  • 10. Dynamic Programming
    • 10.1. Fibonacci Sequence
    • 10.2. Tower of Hanoi
    • 10.3. Longest Common Subsequence
    • 10.4. Quiz
Powered by GitBook

©2023 Emory University - All rights reserved

On this page
  • Balanced Binary Tree
  • Implementation
  • Add() with Swim
  • Remove() with Sink

Was this helpful?

Export as PDF
  1. 2. Priority Queues

2.2. Binary Heap

Operations: swim and sink.

Previous2.1. Simple Priority QueuesNext2.3. Unit Testing

Last updated 2 years ago

Was this helpful?

A binary heap is a PQ that takesO(log⁡n)O(\log n)O(logn)for both add() and remove(), where keys are stored in a balanced binary tree in which every node has a higher priority than its children (when used as a max-PQ).

When should we use a binary heap over a lazy PQ or an eager PQ?

Balanced Binary Tree

A balanced binary tree can be implemented by a list such that given the kkk'th key in the list:

  • The index of its parent: ⌊k2⌋\lfloor \frac{k}{2} \rfloor⌊2k​⌋

  • The index of its left child: 2⋅k2 \cdot k2⋅k.

  • The index of its right child: 2⋅k+12 \cdot k + 12⋅k+1.

  • e.g., given the list [1, 2, 3, 4, 5], 1 is the root, 2 and 3 are the left and right children of 1, and 4 and 5 are the left and right children of 2, respectively.

Is the tree represented by the list [1, 2, 3, 4, 5] balanced?

Implementation

Let us create the class inheriting AbstractPriorityQueue:

public class BinaryHeap<T extends Comparable<T>> extends AbstractPriorityQueue<T> {
    private final List<T> keys;

    public BinaryHeap() {
        this(Comparator.naturalOrder());
    }

    public BinaryHeap(Comparator<T> priority) {
        super(priority);
        keys = new ArrayList<>();
        keys.add(null);
    }

    @Override
    public int size() {
        return keys.size() - 1;
    }
}
  • L11: adding null as the first item makes it easier to calculate the indices of parents and children.

  • L16: keys has the extra item of null from L11 that is not an input key.

How does adding null make the calculation of the indices easier?

Additionally, let us define the helper method compare() that compares two keys in the list:

/**
 * @param i1 the index of the first key in {@link #keys}.
 * @param i2 the index of the second key in {@link #keys}.
 * @return a negative integer, zero, or a positive integer as the first key is
 * less than, equal to, or greater than the second key.
 */
private int compare(int k1, int k2) {
    return priority.compare(keys.get(k1), keys.get(k2));
}
  • L8: calls the compare() method in the member field priority declared in the super class.

Add() with Swim

The add() method in a heap uses the operation called swim as follows:

@Override
public void add(T key) {
    keys.add(key);
    swim(size());
}

private void swim(int k) {
    for (; 1 < k && compare(k / 2, k) < 0; k /= 2)
        Collections.swap(keys, k / 2, k);
}
  • L3: appends the new key to the list.

  • L7-10: keeps swimming until the list becomes a heap:

    • L8: compares the new key to its parent if exists.

How many keys are compared at most for the `add operation?

Remove() with Sink

The remove() method in a heap uses the operation called sink as follows:

@Override
public T remove() {
    if (isEmpty()) return null;
    Collections.swap(keys, 1, size());
    T max = keys.remove(size());
    sink();
    return max;
}

private void sink() {
    for (int k = 1, i = 2; i <= size(); k = i, i *= 2) {
        if (i < size() && compare(i, i + 1) < 0) i++;
        if (compare(k, i) >= 0) break;
        Collections.swap(keys, k, i);
    }
}
  • L3: checks if the heap is empty.

  • L4: swaps the root and the last key in the list.

  • L3: removes the (swapped) last key with the highest priority in this heap.

  • L10-16: keeps sinking until the list becomes a heap:

    • L12: finds the child with a higher priority.

    • L13: breaks if the parent has a higher priority than the child.

    • L14: swaps if the child has a higher priority than the parent.

How many keys are compared at most for the remove operation?

L9: if the new key has a higher priority than its parent, them.

Collections.swap()
BinaryHeap
https://www.slideshare.net/jchoi7s/dsajava-binary-heap-add-238005046
https://www.slideshare.net/jchoi7s/dsajava-binary-heap-remove-238005081