Optimización de Algoritmos con Pilas, Colas Monotónicas y Priority Queues en C++

Evaluación de Expresiones en Notación Polaca Inversa (RPN)

La Notación Polaca Inversa es un método de escritura de expresiones matemáticas donde los operadores siguen a sus operandos. Para resolver este problema de manera eficiente, se utiliza una estructura de datos de tipo pila (LIFO). El algoritmo consiste en iterar sobre los elementos: si encontramos un número, lo apilamos; si encontramos un operador, extraemos los dos últimos elementos, aplicamos la operación y devolvemos el resultado a la pila.

#include <iostream>
#include <vector>
#include <stack>
#include <string>
#include <stdexcept>

using namespace std;

class EvaluadorRPN {
public:
    int evalRPN(vector<string>& tokens) {
        stack<long long> operandos;
        
        for (const string& t : tokens) {
            if (t == "+" || t == "-" || t == "*" || t == "/") {
                long long valorB = operandos.top();
                operandos.pop();
                long long valorA = operandos.top();
                operandos.pop();
                
                if (t == "+") operandos.push(valorA + valorB);
                else if (t == "-") operandos.push(valorA - valorB);
                else if (t == "*") operandos.push(valorA * valorB);
                else if (t == "/") operandos.push(valorA / valorB);
            } else {
                operandos.push(stoll(t));
            }
        }
        return static_cast<int>(operandos.top());
    }
};

Máximo en Ventana Deslizante mediante Colas Monotónicas

Para obtener el valor máximo en cada posición de una ventana que se deslpaza a través de un arreglo, una solución ingenua de O(n*k) no es óptima. Utilizando una cola de doble final (deque) para construir una "Cola Monotónica", podemos reducir la complejidad a O(n). La clave es mantener los elementos en la cola en orden descendente, eliminando aquellos que ya no pueden ser el máximo.

#include <vector>
#include <deque>

using namespace std;

class VentanaOptimizada {
private:
    class ColaMonotonica {
    public:
        deque<int> datos;

        void eliminar(int valor) {
            if (!datos.empty() && valor == datos.front()) {
                datos.pop_front();
            }
        }

        void insertar(int valor) {
            while (!datos.empty() && valor > datos.back()) {
                datos.pop_back();
            }
            datos.push_back(valor);
        }

        int obtenerMaximo() {
            return datos.front();
        }
    };

public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        ColaMonotonica q;
        vector<int> maximos;

        for (int i = 0; i < k; i++) {
            q.insertar(nums[i]);
        }
        maximos.push_back(q.obtenerMaximo());

        for (int i = k; i < nums.size(); i++) {
            q.eliminar(nums[i - k]);
            q.insertar(nums[i]);
            maximos.push_back(q.obtenerMaximo());
        }
        return maximos;
    }
};

Elementos más Frecuentes con Priority Queue

Identificar los K elemenots con mayor frecuencia requiere dos pasos: primero, contar las ocurrencias usando un mapa hash; segundo, seleccionar los elementos más frecuentes. Al usar un min-heap (cola de prioridad) de tamaño K, garantizmaos que solo conservamos los elementos con mayores frecuencias, logrando una eficiencia de O(n log k).

#include <vector>
#include <unordered_map>
#include <queue>

using namespace std;

class AnalizadorFrecuencia {
public:
    struct ComparadorFrec {
        bool operator()(const pair<int, int>& a, const pair<int, int>& b) {
            return a.second > b.second; // Min-heap basado en frecuencia
        }
    };

    vector<int> topKFrequent(vector<int>& nums, int k) {
        unordered_map<int, int> conteo;
        for (int n : nums) {
            conteo[n]++;
        }

        priority_queue<pair<int, int>, vector<pair<int, int>>, ComparadorFrec> minHeap;

        for (auto const& [num, freq] : conteo) {
            minHeap.push({num, freq});
            if (minHeap.size() > k) {
                minHeap.pop();
            }
        }

        vector<int> resultado(k);
        for (int i = k - 1; i >= 0; i--) {
            resultado[i] = minHeap.top().first;
            minHeap.pop();
        }
        return resultado;
    }
};

Etiquetas: cpp algorithms DataStructures stack deque

Publicado el 9-6 03:41