CSE-250 Fall 2022 - Section B - Priority Queues, Heaps

Priority Queues, Heaps

CSE-250 Fall 2022 - Section B

Oct 24, 2022

Textbook: Ch. 18

Priority Queues

PriorityQueue[A <: Ordering]

enqueue(v: A): Unit
Insert a value $v$ into the priority queue.
dequeue: A
Remove the greatest element in the priority queue.
head: A
Peek at the greatest element in the priority queue.

Priority Queues

Operation Lazy Proactive
enqueue $O(1)$ $O(n)$
dequeue $O(n)$ $O(1)$
head $O(n)$ $O(1)$

Can we do better?

Priority Queues

Lazy
Fast Enqueue, Slow Dequeue
Proactive
Slow Enqueue, Fast Dequeue
???
Fast(-ish) Enqueue, Fast(-ish) Dequeue

Idea: Keep the priority queue "kinda" sorted.

Larger items tend to be closer to the front of the list. The closer we are to the front of the list, the more sorted it gets.

Binary Heaps

Challenge: How do we keep track of which elements are still sorted?

Binary Heaps

Idea: Organize the priority queue as a tree

Directed: A directed edge means "≥"

Trees

Child
An adjacent node connected by an out-edge
Leaf
A node with no children
Depth of a node
The number of edges from the root to the node
Depth of a tree
The maximum depth of any node in the tree
Level of a node
The depth + 1

Binary Heaps

Organize the priority queue as a tree

Directed: A directed edge means "≥"

Binary: Max out-degree of 2 (Easy to reason about)

Complete: Every "level" except the last is full

  • Balanced: TBD (loosely, all leaves at ~ same level)
  • Easy to encode in an array (later today)

All nodes in the last level are all-the-way left

Ordering by "≥" gets us a "Max-Heap"

Valid Max-Heaps

Invalid Max-Heaps

Heaps

What is the depth of a binary heap containing $n$ items?

  • Level 1: Up to 1 item
  • Level 2: Up to 2 items
  • Level 3: Up to 4 items
  • Level 4: Up to 8 items
  • Level 5: Up to 16 items
  • Level $i$: Up to $O(2^i)$ items

Heaps

What is the depth of a binary heap containing $n$ items?

$$n = O\left(\sum_{i = 1}^{\ell_{max}} 2^i\right) = O\left(2^{\ell_{max}}\right)$$

$$\ell_{max} = O\left(\log(n)\right)$$

The Heap ADT

enqueue(elem: A) [pushHeap]
Place an item into the heap
dequeue: A [popHeap]
Remove and return the maximal element from the heap
head: A
The maximal element in the heap
length: Int
The number of elements in the heap.

enqueue

Idea: Insert element at next available spot, then fix.

  • Call the insertion point current
  • While current != root and current > parent
    • Swap current with parent
    • Repeat with currentparent

enqueue

enqueue


    def fixUp[A: Ordering](current: Vertex[A]): Unit = 
    {
      if(current.parent.isDefined){
        val parent = current.parent.get
        if( Ordering[A].lt( parent.value, current.value ) ){
          swap(current.value, parent.value)
          fixUp(parent)
        }
      }
    }
  

What's the complexity?

(How many swaps are required?)

dequeue

Idea: Replace root with last element, then fix.

  • Call start with currentroot
  • While current has a child > current
    • Swap current with larger child
    • Repeat with currentchild

dequeue

enqueue


    def fixDown[A: Ordering](current: Vertex[A]): Unit = 
    {
      if(current.leftChild.isDefined){
        val left = current.leftChild.get
        if( Ordering[A].lt( current.value, left.value ) ){
          if(current.rightChild.isDefined){
            val right = current.rightChild.get
            if( Ordering[A].lt( right.value, left.value ) ){
              swap(current.value, left.value); fixDown(left)
            } else {
              swap(current.value, right.value); fixDown(right)
            }
          } else {
            swap(current.value, left.value); fixDown(left)
          }
        } else if(current.rightChild.isDefined) {
          val right = current.rightChild.get
          if( Ordering[A].lt( current.value, right.value ) ){
            swap(current.value, right.value); fixDown(right)
          }
        }
      }
    }
  

What's the complexity?

Priority Queues

Operation Lazy Proactive Heap
enqueue $O(1)$ $O(n)$ $O(\log(n))$
dequeue $O(n)$ $O(1)$ $O(\log(n))$
head $O(n)$ $O(1)$ $O(1)$

Storing Heaps

  • Each layer has a maximum size
  • Each layer grows left-to-right
  • Only the last layer grows

Idea: Use an ArrayBuffer to store the heap.

Storing Heaps

Analysis

Enqueue
Append to ArrayBuffer: $O(n)$, Amortized $O(1)$
fixUp: $O(\log(n))$ fixes, each $O(1)$ = $O(\log(n))$
Total: Amortized $O(\log(n))$, Worst-case $O(n)$
Dequeue
Remove End of ArrayBuffer: $O(1)$
fixDown: $O(\log(n))$ fixes, each $O(1)$ = $O(\log(n))$
Total: Worst-case $O(\log(n))$