Open In App

Heap Data Structure for Competitive Programming

Last Updated : 10 Mar, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Competitive programming needs smart tools to solve problems quickly. One key tool is the Heap Data Structure, which helps organize data in a way that’s super fast. In this article, we’ll break down the Heap, looking at its types, basic moves, and how it’s a big deal in competitive programming. We’ll focus on something called Priority Queue, a special use of Heap, and show you how to use it to solve problems better. It does not matter if you are new to Heap or if you have a little knowledge already, learning it will greatly enhance your problem-solving skills in competitive programming.

Introduction to Heap Data Structure:

A Heap is a specialized tree-based data structure that satisfies the heap property. In simple terms, it’s a way of organizing elements in a hierarchy, where each element has a priority relative to others. In a Heap, the top element is always the one with the highest (or lowest) priority, making it quick to access. There are different types of heaps, but they all share this fundamental idea of efficient data organization.

Note: A Heap is a specialized tree-based data structure that satisfies the heap property. It can be implemented as an array.

Types of Heap Data Structure:

Generally, Heaps can be of two types:

  1. Max-Heap: In a Max-Heap the key present at the root node must be greatest among the keys present at all of its children. The same property must be recursively true for all sub-trees in that Binary Tree.
  2. Min-Heap: In a Min-Heap the key present at the root node must be minimum among the keys present at all of its children. The same property must be recursively true for all sub-trees in that Binary Tree.

Operations of Heap Data Structure:

The Heap Data Structure supports fundamental operations that enable efficient management of its elements. Below are some operations of Heap Data Structure:

1. Heapify:

  • It is the process to rearrange the elements to maintain the property of heap data structure.
  • It takes O(log N) to balance the tree. 

2. Insertion:

  • If we insert a new element into the heap since we are adding a new element into the heap so it will distort the properties of the heap so we need to perform the heapify operation so that it maintains the property of the heap.
  • This operation also takes O(logN) time.

3. getMax (For max-heap) or getMin (For min-heap):

  • It finds the maximum element or minimum element for max-heap and min-heap respectively and as we know minimum and maximum elements will always be the root node itself for min-heap and max-heap respectively.
  • It takes O(1) time.

4. removeMin or removeMax:

  • This operation returns and deletes the maximum element and minimum element from the max-heap and min-heap respectively. In short, it deletes the root element of the heap binary tree.
  • It takes O(1) time.

What is Priority Queue?

A Priority Queue is an abstract data type that stores elements along with their associated priorities, and it allows for efficient retrieval of the element with the highest (or lowest) priority. In simpler terms, a priority queue is a data structure that manages a collection of elements, each assigned a priority, and provides operations to insert elements and remove the element with the highest (or lowest) priority.

Key Characteristics of a Priority Queue:

  • Priority-Based Ordering: Elements in a priority queue are arranged based on their priority, not in the order of their insertion.
  • Efficient Access: The primary purpose of a priority queue is to quickly access and remove the element with the highest (max priority) or lowest (min priority) value.
  • Abstract Data Type: The priority queue abstracts away the specific details of how priorities are assigned or how the elements are stored internally.
  • Implementation: Priority queues can be implemented using various data structures, with heaps being a common choice due to their efficiency in maintaining priorities.

Common Operations on a Priority Queue:

  • Insertion (Push): Add an element to the priority queue with its associated priority.
  • Deletion (Pop): Remove the element with the highest (max priority) or lowest (min priority) value.
  • Peek (Top): Access the element with the highest (max priority) or lowest (min priority) value without removing it.

Priority Queue in C++ for Competitve Programming:

The priority_queue container from the C++ Standard Template Library (STL) provides a convenient way to work with priority queues. Below is a simple guide on using priority_queue in C++ for competitive programming:

Import Necessary Package:

import java.util.PriorityQueue;

Below are the Initialization and methods of Priority Queue in C++:

Initialization and Methods Initialization Max Heap (default) Initialization Min Heap (default)
Initialization std::priority_queue<int> maxHeap; std::priority_queue<int, std::vector<int>, std::greater<int>> minHeap;
Inserting Elements maxHeap.push(5); minHeap.push(5);
Removing Top Element maxHeap.pop(); minHeap.pop();
Accessing Top Element int topElement = maxHeap.top(); int topElement = minHeap.top();
Size and Empty Check int size = maxHeap.size();<br>bool isEmpty = maxHeap.empty(); int size = minHeap.size();<br>bool isEmpty = minHeap.empty();

Custom Comparator in Priority Queue in C++ STL:

Priority Queue can also be used to store pairs inside of it and we can use custom comparators for ordering by first and second element. Refer to the below articles:

Priority Queue in Java for Competitve Programming:

In Java, the PriorityQueue class from the java.util package provides a convenient way to implement a Priority Queue for competitive programming. Here’s a guide on using PriorityQueue in Java:

Import Necessary Package:

import java.util.PriorityQueue;

Below are the Initialization and methods of PriorityQueue in Java:

Initialization and Methods Initialization Max Heap (default) Initialization Min Heap
Initialization PriorityQueue<Integer> maxHeap = new PriorityQueue<>(); PriorityQueue<Integer> minHeap = new PriorityQueue<>(Comparator.naturalOrder());
Inserting Elements maxHeap.add(5); minHeap.add(5);
Accessing Top Element int topElement = maxHeap.peek(); int topElement = minHeap.peek();
Removing Top Element maxHeap.poll(); minHeap.poll();
Size and Empty Check int size = maxHeap.size();<br>boolean isEmpty = maxHeap.isEmpty(); int size = minHeap.size();<br>boolean isEmpty = minHeap.isEmpty();

Custom Comparator in Priority Queue in Java :

We can use a custom comparator to implement a priority queue with specific ordering rules.

Priority Queue in Python for Competitve Programming:

In Python, you can use the heapq module to implement a Priority Queue efficiently. Below is a guide on using heapq in Python for competitive programming:

Import Necessary Module:

import heapq

Below are the Initialization and methods of Priority Queue in Python:

Initialization and Methods Initialization Min Heap (default) Initialization Max Heap (using negation)
Initialization min_heap = [] max_heap = []
Inserting Elements heapq.heappush(min_heap, 5) heapq.heappush(max_heap, -5)
Accessing Top Element top_element_min = min_heap[0] top_element_max = -max_heap[0] # Remember to negate values for max heap
Removing Top Element heapq.heappop(min_heap) heapq.heappop(max_heap)
Size and Empty Check size_min = len(min_heap)
isEmpty_min = not bool(min_heap)
size_max = len(max_heap)
isEmpty_max = not bool(max_heap)

Problem Identification of Priority Queue:

Below are the few problems that will help to identify how or when can we use Priority Queue in competitive programming problem:

Problem 1: Given n energy drinks with values ai​ on a line, where drinking the ith drink ai​ increases stamina by ai​ if is positive, and decreases if ai is negative, and leaving the drink as it is does not affect our stamina. Starting with 0 stamina, determine the maximum number of drinks you can consume without stamina becoming negative at any point, and drinks must be consumed in order.

The problem can be solved in the following way:

  • Sequential Consumption:
    • Consume energy drinks one by one, moving from the leftmost to the rightmost drink.
  • Maintain Count:
    • Keep a running count of the number of drinks consumed.
  • Check Stamina:
    • After consuming each drink, assess whether the stamina becomes negative.
  • Remove Most Negative Drink:
    • If negative stamina occurs, identify the most negatively impactful drink taken so far.
    • Utilize a priority queue to efficiently find and remove the drink with the greatest negative impact on stamina.
  • Track Maximum Drinks:
    • Continuously track the maximum number of drinks consumed without stamina becoming negative.

Below is the implementation of above approach:

C++




#include <bits/stdc++.h>
using namespace std;
 
// Function to solve the given problem
int maxDrinksWithoutNegativeStamina(vector<int>& arr)
{
    long long currentSum = 0; // Current cumulative stamina
 
    int drinksConsumed = 0; // Count of drinks consumed
 
    priority_queue<int>
        negativeElements; // Priority queue for efficiently
                        // removing negative elements
 
    // Iterate through the energy drinks
    for (int i = 0; i < arr.size(); i++) {
        // Current energy value of the drink
        int currentElement = arr[i];
        // Update cumulative stamina
        currentSum += currentElement;
 
        // Increment drinks consumed
        drinksConsumed++;
        // Add negative of the curren element to the
        // priority queue
        negativeElements.push(-currentElement);
 
        // Check and remove elements causing negative
        // stamina
        while (currentSum < 0) {
            // Reduce drinks consumed count
            drinksConsumed--;
         
            // Add the most negatively impactful drink
            currentSum += negativeElements.top();
         
            // Remove the drink from the priority queue
            negativeElements.pop();
        }
    }
 
    // Return the maximum number of drinks consumed without
    // stamina becoming negative
    return drinksConsumed;
}
 
// Driver Code
 
int main()
{
 
    vector<int> energyDrinks = { 2, -3, 5, -1, 6 };
    int result
        = maxDrinksWithoutNegativeStamina(energyDrinks);
    cout << "Maximum number of drinks without negative "
            "stamina: "
        << result << endl;
 
    return 0;
}


Java




import java.util.PriorityQueue;
 
public class MaxDrinksWithoutNegativeStamina {
 
    public static int maxDrinksWithoutNegativeStamina(int[] arr) {
        int currentSum = 0// Current cumulative stamina
        int drinksConsumed = 0// Count of drinks consumed
 
        PriorityQueue<Integer> negativeElements = new PriorityQueue<>();  // Min heap for efficiently removing negative elements
 
        // Iterate through the energy drinks
        for (int currentElement : arr) {
            // Update cumulative stamina
            currentSum += currentElement;
 
            // Increment drinks consumed
            drinksConsumed++;
 
            // Add the negative of the current element to the priority queue
            negativeElements.add(-currentElement);
 
            // Check and remove elements causing negative stamina
            while (currentSum < 0) {
                // Reduce drinks consumed count
                drinksConsumed--;
 
                // Add the most negatively impactful drink
                currentSum += -negativeElements.poll();
            }
        }
 
        // Return the maximum number of drinks consumed without stamina becoming negative
        return drinksConsumed;
    }
 
    public static void main(String[] args) {
        int[] energyDrinks = {2, -3, 5, -1, 6};
        int result = maxDrinksWithoutNegativeStamina(energyDrinks);
        System.out.println("Maximum number of drinks without negative stamina: " + result);
    }
}
 
 
 
// This code is contributed by shivamgupta0987654321


C#




using System;
using System.Collections.Generic;
 
class Program
{
    // Function to solve the given problem
    static int MaxDrinksWithoutNegativeStamina(List<int> arr)
    {
        long currentSum = 0; // Current cumulative stamina
 
        int drinksConsumed = 0; // Count of drinks consumed
 
        PriorityQueue<int> negativeElements =
          new PriorityQueue<int>(Comparer<int>.Create((a, b) => b.CompareTo(a)));
        // Priority queue for efficiently removing negative elements
 
        // Iterate through the energy drinks
        for (int i = 0; i < arr.Count; i++)
        {
            // Current energy value of the drink
            int currentElement = arr[i];
            // Update cumulative stamina
            currentSum += currentElement;
 
            // Increment drinks consumed
            drinksConsumed++;
            // Add negative of the current element to the
            // priority queue
            negativeElements.Enqueue(-currentElement);
 
            // Check and remove elements causing negative
            // stamina
            while (currentSum < 0)
            {
                // Reduce drinks consumed count
                drinksConsumed--;
 
                // Add the most negatively impactful drink
                currentSum += negativeElements.Dequeue();
            }
        }
 
        // Return the maximum number of drinks consumed without
        // stamina becoming negative
        return drinksConsumed;
    }
 
    // Driver Code
    static void Main()
    {
        List<int> energyDrinks = new List<int> { 2, -3, 5, -1, 6 };
        int result = MaxDrinksWithoutNegativeStamina(energyDrinks);
        Console.WriteLine($"Maximum number of drinks without negative stamina: {result}");
 
        Console.ReadLine();
    }
}
 
// Priority Queue Implementation
public class PriorityQueue<T>
{
    private List<T> heap;
    private readonly IComparer<T> comparer;
 
    public PriorityQueue() : this(null) { }
 
    public PriorityQueue(IComparer<T> comparer) : this(16, comparer) { }
 
    public PriorityQueue(int capacity, IComparer<T> comparer)
    {
        this.heap = new List<T>(capacity);
        this.comparer = comparer ?? Comparer<T>.Default;
    }
 
    public void Enqueue(T item)
    {
        heap.Add(item);
        int i = heap.Count - 1;
        while (i > 0)
        {
            int parent = (i - 1) / 2;
            if (comparer.Compare(heap[parent], heap[i]) <= 0)
                break;
 
            Swap(i, parent);
            i = parent;
        }
    }
 
    public T Dequeue()
    {
        int count = heap.Count - 1;
        T root = heap[0];
        heap[0] = heap[count];
        heap.RemoveAt(count);
 
        count--;
        int i = 0;
        while (true)
        {
            int left = i * 2 + 1;
            if (left > count)
                break;
 
            int right = left + 1;
            if (right <= count && comparer.Compare(heap[right], heap[left]) < 0)
                left = right;
 
            if (comparer.Compare(heap[left], heap[i]) >= 0)
                break;
 
            Swap(i, left);
            i = left;
        }
        return root;
    }
 
    public T Peek()
    {
        return heap[0];
    }
 
    public int Count
    {
        get { return heap.Count; }
    }
 
    private void Swap(int i, int j)
    {
        T tmp = heap[i];
        heap[i] = heap[j];
        heap[j] = tmp;
    }
}
//THis code is contributed by Adarsh


Javascript




// Function to solve the given problem
function maxDrinksWithoutNegativeStamina(arr) {
    let currentSum = 0; // Current cumulative stamina
    let drinksConsumed = 0; // Count of drinks consumed
    const negativeElements = []; // Array to store negative elements
 
    // Iterate through the energy drinks
    for (let i = 0; i < arr.length; i++) {
        // Current energy value of the drink
        const currentElement = arr[i];
        // Update cumulative stamina
        currentSum += currentElement;
        // Increment drinks consumed
        drinksConsumed++;
        // Add negative of the current element to the array
        negativeElements.push(-currentElement);
 
        // Check and remove elements causing negative stamina
        while (currentSum < 0) {
            // Reduce drinks consumed count
            drinksConsumed--;
            // Add the most negatively impactful drink
            currentSum += negativeElements.pop();
        }
    }
 
    // Return the maximum number of drinks consumed without stamina becoming negative
    return drinksConsumed;
}
 
// Driver code
const energyDrinks = [2, -3, 5, -1, 6];
const result = maxDrinksWithoutNegativeStamina(energyDrinks);
console.log("Maximum number of drinks without negative stamina: ", result);


Python3




import heapq
 
def max_drinks_without_negative_stamina(arr):
    current_sum = 0  # Current cumulative stamina
    drinks_consumed = 0  # Count of drinks consumed
 
    negative_elements = []  # Min heap for efficiently removing negative elements
 
    # Iterate through the energy drinks
    for current_element in arr:
        # Update cumulative stamina
        current_sum += current_element
 
        # Increment drinks consumed
        drinks_consumed += 1
 
        # Add the negative of the current element to the priority queue
        heapq.heappush(negative_elements, -current_element)
 
        # Check and remove elements causing negative stamina
        while current_sum < 0:
            # Reduce drinks consumed count
            drinks_consumed -= 1
 
            # Add the most negatively impactful drink
            current_sum += -heapq.heappop(negative_elements)
 
    # Return the maximum number of drinks consumed without stamina becoming negative
    return drinks_consumed
 
# Driver Code
energy_drinks = [2, -3, 5, -1, 6]
result = max_drinks_without_negative_stamina(energy_drinks)
print(f"Maximum number of drinks without negative stamina: {result}")


Output

Maximum number of drinks without negative stamina: 4



Problem 2: Given an array of n integers a1, a2,… an and a integer k. You can perform the following operation any number of times: Choose an index i within the range 1<=i<=n and set ai to ai​. The task is to the value a1+ a2+…+ak minimum among all non-empty prefix sums of array a.

​The problem can be solved in the following way:

  • Greedy Approach:
    • Utilize a greedy strategy to solve the problem.
  • Independent Cases:
    • Address the problem in two independent cases.
  • Case 1: Iterate from k to 1:
    • For each index from k to 1, check if the prefix sum of i is less than the prefix sum of k.
    • If true, apply the operation on the maximal element in the segment [i+1, k]. Since performing an operation to any element in the segment [1,x] does nothing on prefix sum of i and k.
  • Case 2: Iterate from k+1 to n:
    • For each index from k+1 to n, check if the prefix sum of i is less than the prefix sum of k.
    • If true, apply the operation on the minimal element in the segment [k+1, i]. Since performing an operation to any element in the segment [i+1,n] does nothing on prefix sum of i and k.

Below is the implementation of above approach:

C++




#include <bits/stdc++.h>
using namespace std;
 
 
// Function to solve the given problem
void solve(vector<int> a, int n, int k) {
    vector<int> b(n + 1); // Array to store cumulative prefix sums
    int ans = 0; // Variable to store the final answer
    int x = 0, y = 0, z = 1e18; // Temporary variables for calculations
 
    priority_queue<int, vector<int>, greater<int>> pq; // Min heap for Case 2
    priority_queue<int> pq2; // Max heap for Case 1
 
    // Calculate cumulative prefix sums
    for (int i = 1; i <= n; i++) {
        b[i] += b[i - 1] + a[i-1];
    }
 
    // Case 1: Iterate from k to 1
    for (int i = k; i > 0; i--) {
        // Check if the prefix sum of i is less than the prefix sum of k
        while (b[i] < b[k]) {
        // Increment answer
            ans++;
         
        // Get the maximal element in the segment [i+1, k]
            int m = pq2.top();
         
        // Update prefix sum
            b[k] = b[k] - 2 * m;
            pq2.pop(); // Remove the maximal element from the priority queue
        }
        pq2.push(a[i-1]); // Add the current element to the max heap
    }
 
    // Case 2: Iterate from k+1 to n
    for (int i = k + 1; i <= n; i++) {
        y = y + a[i-1];
        pq.push(a[i-1]);
        // Check if the prefix sum of i is less than the prefix sum of k
        while (y < 0) {
        // Get the minimal element in the segment [k+1, i]
            int m = pq.top();
         
        // Update prefix sum
            y = y - 2 * m;
         
        // Increment answer
            ans++;
         
        // Remove the minimal element from the priority queue
            pq.pop();
        }
    }
 
    cout << ans << "\n"; // Output the final answer
}
 
int main() {
    // Example usage
    vector<int> array = {3, -2, 5, -1, -4};
    int n = array.size();
    int k = 3;
    solve(array, n, k);
 
    return 0;
}


Java




import java.util.*;
 
public class Main {
    // Function to solve the given problem
    static void solve(ArrayList<Integer> a, int n, int k) {
        ArrayList<Integer> b = new ArrayList<>(Collections.nCopies(n + 1, 0));  // Array to store cumulative prefix sums
        int ans = 0// Variable to store the final answer
        int x = 0, y = 0, z = (int) 1e18;  // Temporary variables for calculations
 
        PriorityQueue<Integer> pq = new PriorityQueue<>();  // Min heap for Case 2
        PriorityQueue<Integer> pq2 = new PriorityQueue<>(Collections.reverseOrder());  // Max heap for Case 1
 
        // Calculate cumulative prefix sums
        for (int i = 1; i <= n; i++) {
            b.set(i, b.get(i - 1) + a.get(i - 1));
        }
 
        // Case 1: Iterate from k to 1
        for (int i = k; i > 0; i--) {
            // Check if the prefix sum of i is less than the prefix sum of k
            while (b.get(i) < b.get(k)) {
                ans++;
 
                // Get the maximal element in the segment [i+1, k]
                int m = pq2.poll();
 
                // Update prefix sum
                b.set(k, b.get(k) - 2 * m);
            }
            pq2.add(a.get(i - 1));  // Add the current element to the max heap
        }
 
        // Case 2: Iterate from k+1 to n
        for (int i = k + 1; i <= n; i++) {
            y = y + a.get(i - 1);
            pq.add(a.get(i - 1));
            // Check if the prefix sum of i is less than the prefix sum of k
            while (y < 0) {
                // Get the minimal element in the segment [k+1, i]
                int m = pq.poll();
 
                // Update prefix sum
                y = y - 2 * m;
 
                ans++;
 
                // Remove the minimal element from the priority queue
            }
        }
 
        System.out.println(ans); 
    }
 
    public static void main(String[] args) {
        ArrayList<Integer> array = new ArrayList<>(Arrays.asList(3, -2, 5, -1, -4));
        int n = array.size();
        int k = 3;
        solve(array, n, k);
    }
}


C#




using System;
using System.Collections.Generic;
 
class Program {
    // Function to solve the given problem
    static void Solve(List<int> a, int n, int k)
    {
        List<int> b = new List<int>(
            new int[n + 1]); // List to store cumulative
                             // prefix sums
        int ans = 0; // Variable to store the final answer
        int y = 0; // Temporary variable for calculations
 
        PriorityQueue<int> pq
            = new PriorityQueue<int>(); // Min heap for Case
                                        // 2
        PriorityQueue<int> pq2 = new PriorityQueue<int>(
            (p, q) => q.CompareTo(p)); // Max heap for Case 1
 
        // Calculate cumulative prefix sums
        for (int i = 1; i <= n; i++) {
            b[i] = b[i - 1] + a[i - 1];
        }
 
        // Case 1: Iterate from k to 1
        for (int i = k; i > 0; i--) {
            // Check if the prefix sum of i is less than the
            // prefix sum of k
            while (b[i] < b[k]) {
                ans++;
 
                // Get the maximal element in the segment
                // [i+1, k]
                int m = pq2.Peek();
 
                // Update prefix sum
                b[k] = b[k] - 2 * m;
                pq2.Pop(); // Remove the maximal element
                           // from the priority queue
            }
            pq2.Push(a[i - 1]); // Add the current element
                                // to the max heap
        }
 
        // Case 2: Iterate from k+1 to n
        for (int i = k + 1; i <= n; i++) {
            y = y + a[i - 1];
            pq.Push(a[i - 1]);
            // Check if the prefix sum of i is less than the
            // prefix sum of k
            while (y < 0) {
                // Get the minimal element in the segment
                // [k+1, i]
                int m = pq.Peek();
 
                // Update prefix sum
                y = y - 2 * m;
 
                ans++;
 
                // Remove the minimal element from the
                // priority queue
                pq.Pop();
            }
        }
 
        Console.WriteLine(ans);
    }
 
    static void Main(string[] args)
    {
        // Example usage
        List<int> array = new List<int>{ 3, -2, 5, -1, -4 };
        int n = array.Count;
        int k = 3;
        Solve(array, n, k);
    }
}
 
// Priority Queue Implementation
public class PriorityQueue<T> {
    private List<T> data;
    private readonly Comparison<T> comparison;
 
    public PriorityQueue() { data = new List<T>(); }
 
    public PriorityQueue(Comparison<T> comparison)
        : this()
    {
        this.comparison = comparison;
    }
 
    public void Push(T item)
    {
        data.Add(item);
        int ci = data.Count - 1;
        while (ci > 0) {
            int pi = (ci - 1) / 2;
            if (Compare(data[ci], data[pi]) >= 0)
                break;
            T tmp = data[ci];
            data[ci] = data[pi];
            data[pi] = tmp;
            ci = pi;
        }
    }
 
    public T Peek()
    {
        if (data.Count == 0)
            throw new InvalidOperationException(
                "Queue empty");
        return data[0];
    }
 
    public T Pop()
    {
        if (data.Count == 0)
            throw new InvalidOperationException(
                "Queue empty");
        T pop = data[0];
        data[0] = data[data.Count - 1];
        data.RemoveAt(data.Count - 1);
 
        int ci = 0;
        while (ci < data.Count) {
            int lci = 2 * ci + 1;
            int rci = 2 * ci + 2;
            if (lci >= data.Count)
                break;
            int mini = lci;
            if (rci < data.Count
                && Compare(data[rci], data[lci]) < 0)
                mini = rci;
            if (Compare(data[ci], data[mini]) <= 0)
                break;
            T tmp = data[ci];
            data[ci] = data[mini];
            data[mini] = tmp;
            ci = mini;
        }
 
        return pop;
    }
 
    public int Count
    {
        get { return data.Count; }
    }
 
    private int Compare(T i, T j)
    {
        if (comparison != null)
            return comparison(i, j);
        return Comparer<T>.Default.Compare(i, j);
    }
}


Javascript




function solve(a, n, k) {
  let b = Array(n + 1).fill(0);  // Array to store cumulative prefix sums
  let ans = 0;  // Variable to store the final answer
  let x = 0, y = 0, z = 1e18;  // Temporary variables for calculations
 
  let pq = [];  // Min heap for Case 2
  let pq2 = [];  // Max heap for Case 1
 
  // Calculate cumulative prefix sums
  for (let i = 1; i <= n; i++) {
    b[i] = b[i - 1] + a[i - 1];
  }
 
  // Case 1: Iterate from k to 1
  for (let i = k; i > 0; i--) {
    // Check if the prefix sum of i is less than the prefix sum of k
    while (b[i] < b[k]) {
      ans += 1;
 
      // Get the maximal element in the segment [i+1, k]
      let m = pq2.pop();
 
      // Update prefix sum
      b[k] = b[k] - 2 * m;
    }
 
    pq2.push(a[i - 1]);  // Add the current element to the max heap
  }
 
  // Case 2: Iterate from k+1 to n
  for (let i = k + 1; i <= n; i++) {
    y = y + a[i - 1];
    pq.push(a[i - 1]);
 
    // Check if the prefix sum of i is less than the prefix sum of k
    while (y < 0) {
      // Get the minimal element in the segment [k+1, i]
      let m = pq.pop();
 
      // Update prefix sum
      y = y - 2 * m;
 
      ans += 1;
    }
  }
 
  console.log(ans);  // Output the final answer
}
 
let array = [3, -2, 5, -1, -4];
let n = array.length;
let k = 3;
solve(array, n, k);


Python3




import heapq
 
def solve(a, n, k):
    b = [0] * (n + 1# Array to store cumulative prefix sums
    ans = 0  # Variable to store the final answer
    x, y, z = 0, 0, 1e18  # Temporary variables for calculations
 
    pq = []  # Min heap for Case 2
    pq2 = []  # Max heap for Case 1
 
    # Calculate cumulative prefix sums
    for i in range(1, n + 1):
        b[i] = b[i - 1] + a[i - 1]
 
    # Case 1: Iterate from k to 1
    for i in range(k, 0, -1):
        # Check if the prefix sum of i is less than the prefix sum of k
        while b[i] < b[k]:
            # Increment answer
            ans += 1
 
            # Get the maximal element in the segment [i+1, k]
            m = heapq.heappop(pq2)
 
            # Update prefix sum
            b[k] = b[k] - 2 * m
 
        heapq.heappush(pq2, a[i - 1])  # Add the current element to the max heap
 
    # Case 2: Iterate from k+1 to n
    for i in range(k + 1, n + 1):
        y = y + a[i - 1]
        heapq.heappush(pq, a[i - 1])
 
        # Check if the prefix sum of i is less than the prefix sum of k
        while y < 0:
            # Get the minimal element in the segment [k+1, i]
            m = heapq.heappop(pq)
 
            # Update prefix sum
            y = y - 2 * m
 
            # Increment answer
            ans += 1
 
    print(ans)  # Output the final answer
 
# Example usage
array = [3, -2, 5, -1, -4]
n = len(array)
k = 3
solve(array, n, k)


Output

3



Priority Queue Use Cases in Competitive Programming:

Here are some common use cases for priority queues in competitive programming:

1. Dijkstra’s Algorithm:

Priority queues are commonly employed to implement Dijkstra’s algorithm for finding the shortest paths in a graph. The priority queue efficiently selects the vertex with the smallest distance at each step. It is also used in solving problems which are variation of Dijkstra’s Algorithm

2. Prim’s Algorithm:

Similar to Dijkstra’s, priority queues play a crucial role in implementing Prim’s algorithm for finding the minimum spanning tree in a weighted graph. The priority queue helps choose the edge with the smallest weight at each iteration.

3. Median Maintenance:

Heaps can be employed to efficiently find the median of a stream of numbers, a common problem in competitive programming. By maintaining two heaps (a max heap and a min heap), the median can be efficiently updated as new elements are added.

4. Kth Largest/Smallest Element:

Priority queues help find the Kth largest or smallest element efficiently in an array or stream of numbers. For example, a max heap can be used to find the Kth largest element, while a min heap can find the Kth smallest element.

5. Huffman Coding:

Priority queues are crucial in Huffman coding, a widely used algorithm for data compression. The algorithm builds a variable-length prefix coding tree, and a priority queue helps in efficiently merging nodes based on their frequencies.

6. Task Scheduling:

Competitive programming problems often involve scheduling tasks based on their priority or execution time. A priority queue helps efficiently manage and execute tasks in the order of their priority.

Practice Problems of Heap Data Structure for Competitive Programming:

Here are some practice problems involving heap data structures that you can use to enhance your skills in competitive programming:

Problem

Minimum characters required to be removed to make frequency of each character unique

Minimize the sum calculated by repeatedly removing any two elements and inserting their sum to the Array

Maximum score possible from an array with jumps of at most length K

Length of Smallest Subsequence such that sum of elements is greater than equal to K

Minimize Sum of an Array by at most K reductions

Maximize product of sum of speeds of K workers and their minimum efficiency

Maximum possible sub-array sum after at most X swaps

Maximize the profit after selling the tickets

Minimum increment/decrement to make array non-Increasing

Minimum cost path from source node to destination node via K intermediate nodes



Like Article
Suggest improvement
Share your thoughts in the comments

Similar Reads