Heap Applications

CSCI 1933 – Introduction to Algorithms and Data Structures
Adriana Picoral

Heap Applications

A heap is the best choice of data structure when we need quick access to the largest or smallest value in a queue. Some applications are:

  • Priority queues – Task scheduler, ER triage system
  • Heapsort – pretty good runtime, in-place, no quadratic worst-case
  • Graph algorithms, like shortest-path

ER triage system

  • Patients arrive with severity levels (1–5)
  • Use a max-heap (priority queue) to always treat the most critical patient next
  • Add complexity by having patients “timeout” if waiting too long

Discuss in small groups:

  • what classes do we need for this?
  • what data structure are we using?

RunER

ER is empty
4 patients waiting.
Next patient to be seen: Patient severity: 5, arrival time: 09:41:30.597749

Seeing patients: 
Patient severity: 5, arrival time: 09:41:30.597749
Patient severity: 4, arrival time: 09:41:30.588172
Patient severity: 2, arrival time: 09:41:30.597763
Patient severity: 1, arrival time: 09:41:30.597723
public class RunER {
    public static void main(String[] args) {
        Triage er = new Triage();
        System.out.println(er);

        er.insert(new Patient(4));
        er.insert(new Patient(1));
        er.insert(new Patient(5));
        er.insert(new Patient(2));

        System.out.println(er);

        System.out.println("Seeing patients: ");
        while (!er.isEmpty()) {
            System.out.println(er.seePatient());
        }
    }
}

Possible Solution

import java.time.LocalTime;
import java.time.Duration;

public class Patient implements Comparable<Patient> {
    private int severity;
    private LocalTime arrivalTime;

    public Patient(int severity) {
        if (!validSeverity(severity)) throw new RuntimeException("severity must be 1-5");
        this.severity = severity;
        arrivalTime = LocalTime.now();
    }

    private boolean validSeverity(int severity) {
        return severity >= 1 && severity <= 5;
    }

    public int getSeverity() { return severity; }
    public boolean setSeverity(int severity) {
        if (!validSeverity(severity)) return false;
        this.severity = severity;
        return true;
    }

    public LocalTime getArrivalTime() { return arrivalTime; }
    public double getWaitTimeMinutes() {
        Duration wait = Duration.between(arrivalTime,LocalTime.now());
        return wait.toMinutes();
    }

    public int compareTo(Patient other) {
        if (other == null) return 1;
        if (this == other) return 0;
        return this.severity - other.severity;
    }

    public String toString() {
        String out = "Patient severity: " + severity;
        out += ", arrival time: " + arrivalTime;
        return out;
    }
}

Possible Solution

public class Triage {
    private Patient[] patients;
    private int patientCount = 0;
    private int erCapacity = 100;

    public Triage() {
        patients = new Patient[erCapacity];
    }

    public int getPatientCount() {
        return patientCount;
    }

    private int parent(int i)    { return (i - 1) / 2; }
    private int leftChild(int i) { return 2 * i + 1; }
    private int rightChild(int i){ return 2 * i + 2; }

    private boolean hasParent(int i)     { return i > 0; }
    private boolean hasLeftChild(int i)  { return leftChild(i) < patientCount; }
    private boolean hasRightChild(int i) { return rightChild(i) < patientCount; }

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


    // Returns the minimum element (root) without removing it — O(1)
    public Patient nextPatientInfo() {
        if (isEmpty()) throw new IllegalStateException("ER is empty");
        return patients[0];
    }

    // Inserts a new value and restores the heap property — O(log n)
    public void insert(Patient value) {
        if (patientCount == erCapacity) throw new IllegalStateException("ER is full");
        patients[patientCount] = value;   // place at the next open spot
        patientCount++;
        bubbleUp(patientCount - 1);   // restore heap property upward
    }

    // Removes and returns the minimum element — O(log n)
    public Patient seePatient() {
        if (isEmpty()) throw new IllegalStateException("ER is empty");
        Patient min = patients[0];
        patients[0] = patients[patientCount - 1];
        patientCount--;
        bubbleDown(0);
        return min;
    }

    private void swap(int a, int b) {
        Patient temp = patients[a];
        patients[a] = patients[b];
        patients[b] = temp;
    }

    private void bubbleUp(int i) {
        while (hasParent(i) && patients[i].compareTo(patients[parent(i)]) > 0) {
            swap(i, parent(i));
            i = parent(i);
        }
    }

    // used after pop min
    private void bubbleDown(int i) {
        while (hasLeftChild(i)) {
            int largerChild = leftChild(i);

            // find the smaller of the two children
            if (hasRightChild(i) && patients[rightChild(i)].compareTo(patients[leftChild(i)]) > 0) {
                largerChild = rightChild(i);
            }

            if (patients[i].compareTo(patients[largerChild])  >= 0) return;

            swap(i, largerChild);
            i = largerChild;
        }
    }


    public String toString() {
        if (isEmpty()) return "ER is empty";
        String out = "Patient list:\n";
        for (int i = 0; i < patientCount; i++) out += patients[i] + "\n";
        out += "\nNext patient to be seen: " + nextPatientInfo().toString();
        return out.trim();
    }

}