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.
Table of Content
- Introduction to Heap Data Structure
- Types of Heap Data Structure
- Operations of Heap Data Structure
- What is Priority Queue?
- Priority Queue in C++ for Competitve Programming
- Priority Queue in Java for Competitve Programming
- Priority Queue in Python for Competitve Programming
- Problem Identification of Priority Queue
- Priority Queue Use Cases in Competitive Programming
- Practice Problems of Heap Data Structure for 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:
- 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.
- 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:
- Custom Comparator in Priority_queue in C++ STL
- Priority queue of pairs in C++ with ordering by first and second element
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:
#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;
} |
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 |
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 |
// 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);
|
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}" )
|
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:
#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;
} |
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);
}
} |
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);
}
} |
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); |
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) |
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: