Day 22: Priority Queues

Day 22: Priority Queues

Priority queues are an extension of heaps, designed to handle elements based on their assigned priorities efficiently. Today’s challenges showcased their versatility, tackling problems such as reorganizing strings, identifying top elements, and optimizing resource allocation in various scenarios.

Check out my GitHub repository for all the solutions and progress updates at: 100 Days of DSA

Stay tuned as I share the problems, their solutions, and the thought process behind each one! 🚀👩‍💻


1. Reorganize String

This program reorganizes a string so that no two adjacent characters are the same using a priority queue (max heap). It calculates character frequencies, stores them in a max heap, and repeatedly adds the most frequent character to the result string while ensuring no character is repeated consecutively. If the reorganization is possible, it returns the result string; otherwise, it returns an empty string.

Code:

#include <iostream>
#include <queue>
#include <string>
#include <unordered_map>
using namespace std;

#define MAX 26      // Maximum number of unique lowercase English letters

struct CharFrequency {
    char character;
    int frequency;
    bool operator<(const CharFrequency &other) const {
        return frequency < other.frequency; // Max Heap property
    }
};

string reorganize_string(const string &s) {
    unordered_map<char, int> freq_map;
    for (char c : s) {
        freq_map[c]++;
    }

    // Priority Queue (Max Heap)
    priority_queue<CharFrequency> max_heap;

    for (auto &entry : freq_map) {
        max_heap.push({entry.first, entry.second});
    }

    string result = "";
    CharFrequency prev = {'#', 0};          // Dummy previous character

    while (!max_heap.empty()) {
        CharFrequency current = max_heap.top();
        max_heap.pop();

        result += current.character;

        if (prev.frequency > 0) {
            max_heap.push(prev);
        }

        current.frequency--;
        prev = current;
    }

    // Check if the reorganization was successful
    return result.size() == s.size() ? result : "";
}

int main() {
    string s = "aab";
    string result = reorganize_string(s);
    if (!result.empty()) {
        cout << "Reorganized string: " << result << endl;
    } else {
        cout << "Reorganization not possible." << endl;
    }
    return 0;
}

Output:


2. Top K Frequent Elements

This program finds the top k frequent elements in an array using a priority queue (min heap). It calculates the frequency of each element using an unordered map, then maintains a heap of size k to store the most frequent elements. After processing all elements, the heap contains the top k frequent elements, which are extracted and stored in the result array.

Code:


#include <iostream>
#include <unordered_map>
#include <queue>
using namespace std;

#define MAX 1000 // Maximum size for input array

struct ElementFrequency {
    int element;
    int frequency;
    bool operator<(const ElementFrequency &other) const {
        return frequency > other.frequency; // Min Heap property
    }
};

void top_k_frequent(int nums[], int n, int k, int result[]) {
    unordered_map<int, int> freqMap;

    // Step 1: Calculate frequencies
    for (int i = 0; i < n; i++) {
        freqMap[nums[i]]++;
    }

    // Step 2: Use a Min Heap to store the top k elements
    priority_queue<ElementFrequency> min_heap;

    for (auto &entry : freqMap) {
        min_heap.push({entry.first, entry.second});
        if (min_heap.size() > k) {
            min_heap.pop();
        }
    }

    // Step 3: Extract elements from the Min Heap
    int index = 0;
    while (!min_heap.empty()) {
        result[index++] = min_heap.top().element;
        min_heap.pop();
    }
}

int main() {
    int nums[MAX] = {1, 1, 1, 2, 2, 3};
    int n = 6; // Number of elements in the array
    int k = 2; // Number of top frequent elements to find
    int result[MAX];
    top_k_frequent(nums, n, k, result);
    cout << "Top " << k << " frequent elements: ";
    for (int i = 0; i < k; i++) {
        cout << result[i] << " ";
    }
    return 0;
}

Output:


3. Sort Characters by Frequency

This program sorts the characters of a string in descending order of frequency using a priority queue (max heap). It first calculates character frequencies using an unordered map, then adds these characters and their frequencies to the heap. Characters are extracted from the heap and appended to the result string based on their frequency, forming the sorted output.

Code:

#include <iostream>
#include <string>
#include <unordered_map>
#include <queue>
using namespace std;

#define MAX 256 // Maximum number of unique characters in ASCII

struct CharFrequency {
    char character;
    int frequency;
    bool operator<(const CharFrequency &other) const {
        return frequency < other.frequency; // Max Heap property
    }
};

string sort_by_frequency(const string &s) {
    unordered_map<char, int> freq_map;

    // Step 1: Count frequency of each character
    for (char c : s) {
        freq_map[c]++;
    }

    // Step 2: Use a max heap to sort characters by frequency
    priority_queue<CharFrequency> max_heap;

    for (auto &entry : freq_map) {
        max_heap.push({entry.first, entry.second});
    }

    // Step 3: Build the sorted string
    string result = "";
    while (!max_heap.empty()) {
        CharFrequency top = max_heap.top();
        max_heap.pop();

        for (int i = 0; i < top.frequency; i++) {
            result += top.character;
        }
    }

    return result;
}

int main() {
    string s = "seventeen";
    string result = sort_by_frequency(s);
    cout << "String sorted by frequency: " << result << endl;
    return 0;
}

Output:


4. K Closest Points to the Origin

This program finds the k closest points to the origin by calculating the squared distance of each point from the origin and storing them in a priority queue (min heap). It ensures that only the k closest points remain in the heap. After processing all points, the program extracts and prints the k closest points from the heap.

Code:

#include <iostream>
#include <queue>
#include <cmath>
#include <unordered_map>
using namespace std;

#define MAX 100     // Maximum size of the points array

struct Point {
    int x, y;

    // Calculate distance squared (no need for square root to compare distances)
    int distance() const {
        return x * x + y * y;
    }

    bool operator<(const Point& other) const {
        return distance() > other.distance();   // Min Heap property (smallest distance at the top)
    }
};

void k_closest_points(int points[][2], int n, int k, int result[][2]) {
    priority_queue<Point> min_heap;

    // Step 1: Add points to the min heap based on their distance from the origin
    for (int i = 0; i < n; i++) {
        min_heap.push({points[i][0], points[i][1]});
        if (min_heap.size() > k) {
            min_heap.pop();  // Maintain only the k closest points
        }
    }

    // Step 2: Extract the k closest points from the heap and store them in the result
    int index = 0;
    while (!min_heap.empty()) {
        Point top = min_heap.top();
        min_heap.pop();
        result[index][0] = top.x;
        result[index][1] = top.y;
        index++;
    }
}

int main() {
    int points[MAX][2] = {{1, 3}, {-2, 2}, {5, 8}, {0, 1}};
    int n = 4;  // Number of points
    int k = 2;  // Number of closest points to find
    int result[MAX][2];
    k_closest_points(points, n, k, result);
    cout << "K closest points to the origin are:\n";
    for (int i = 0; i < k; i++) {
        cout << "(" << result[i][0] << ", " << result[i][1] << ")\n";
    }
    return 0;
}

Output:


5. Connect N Ropes to Minimize Cost

This program minimizes the cost of connecting multiple ropes by using a priority queue (min heap). It repeatedly extracts the two shortest ropes, connects them, and adds the cost to the total. The new connected rope is then pushed back into the heap. This process continues until only one rope remains, and the total cost of connecting all ropes is output.

Code:

#include <iostream>
#include <queue>
using namespace std;

#define MAX 100 // Maximum number of ropes

void connect_ropes(int ropes[], int n) {
    priority_queue<int, vector<int>, greater<int>> min_heap; // Min heap to store rope lengths

    // Step 1: Insert all the ropes into the min heap
    for (int i = 0; i < n; i++) {
        min_heap.push(ropes[i]);
    }

    int tot_cost = 0;

    // Step 2: Connect ropes while more than one rope remains
    while (min_heap.size() > 1) {
        // Get the two shortest ropes
        int first = min_heap.top();
        min_heap.pop();
        int second = min_heap.top();
        min_heap.pop();

        // The cost to connect them is the sum of their lengths
        int cost = first + second;
        tot_cost += cost;

        // Insert the new rope back into the heap
        min_heap.push(cost);
    }

    // Step 3: Output the minimum total cost
    cout << "Minimum cost to connect all ropes: " << tot_cost << endl;
}

int main() {
    int ropes[MAX] = {4, 3, 2, 6}; 
    int n = 4;  // Number of ropes
    connect_ropes(ropes, n);
    return 0;
}

Output:


Day 22 highlighted the power of priority queues in solving optimization and frequency-related problems efficiently. By understanding how to prioritize elements dynamically, I gained valuable insights into how this data structure can be applied to real-world scenarios and competitive programming challenges. 🚀