EDITAR: Esto falla en la restricción de "espacio constante" - básicamente duplica el espacio requerido. Sin embargo, dudo mucho que haya una solución que no haga eso, sin arruinar la complejidad del tiempo de ejecución en alguna parte (por ejemplo, hacer push / pop O (n)). Tenga en cuenta que esto no cambia la complejidad del espacio requerido, por ejemplo, si tiene una pila con requisitos de espacio O (n), seguirá siendo O (n) solo con un factor constante diferente.
Solución de espacio no constante
Mantenga una pila "duplicada" de "el mínimo de todos los valores inferiores en la pila". Cuando saque la pila principal, saque también la pila mínima. Cuando empuja la pila principal, empuja el nuevo elemento o el mínimo actual, el que sea menor. getMinimum()
luego se implementa como just minStack.peek()
.
Entonces, usando su ejemplo, tendríamos:
Real stack Min stack
5 --> TOP 1
1 1
4 2
6 2
2 2
Después de hacer estallar dos veces, obtienes:
Real stack Min stack
4 2
6 2
2 2
Avíseme si esto no es suficiente información. Es simple cuando lo asimilas, pero al principio puede ser necesario rascarte un poco la cabeza :)
(La desventaja, por supuesto, es que duplica el requisito de espacio. Sin embargo, el tiempo de ejecución no sufre significativamente, es decir, sigue siendo la misma complejidad).
EDITAR: Hay una variación que es un poco más complicada, pero tiene mejor espacio en general. Todavía tenemos la pila mínima, pero solo salimos de ella cuando el valor que sacamos de la pila principal es igual al de la pila mínima. Solo empujamos a la pila mínima cuando el valor que se empuja a la pila principal es menor o igual que el valor mínimo actual. Esto permite valores mínimos duplicados. getMinimum()
sigue siendo sólo una operación mínima. Por ejemplo, tomando la versión original y presionando 1 nuevamente, obtendríamos:
Real stack Min stack
1 --> TOP 1
5 1
1 2
4
6
2
Saliendo de los salientes anteriores de ambas pilas porque 1 == 1, dejando:
Real stack Min stack
5 --> TOP 1
1 2
4
6
2
Salir de nuevo solo aparece en la pila principal, porque 5> 1:
Real stack Min stack
1 1
4 2
6
2
Al volver a aparecer, aparecen ambas pilas porque 1 == 1:
Real stack Min stack
4 2
6
2
Esto termina con la misma complejidad de espacio en el peor de los casos (el doble de la pila original) pero mucho mejor uso del espacio si rara vez obtenemos un "nuevo mínimo o igual".
EDITAR: Aquí hay una implementación del esquema malvado de Pete. No lo he probado a fondo, pero creo que está bien :)
using System.Collections.Generic;
public class FastMinStack<T>
{
private readonly Stack<T> stack = new Stack<T>();
// Could pass this in to the constructor
private readonly IComparer<T> comparer = Comparer<T>.Default;
private T currentMin;
public T Minimum
{
get { return currentMin; }
}
public void Push(T element)
{
if (stack.Count == 0 ||
comparer.Compare(element, currentMin) <= 0)
{
stack.Push(currentMin);
stack.Push(element);
currentMin = element;
}
else
{
stack.Push(element);
}
}
public T Pop()
{
T ret = stack.Pop();
if (comparer.Compare(ret, currentMin) == 0)
{
currentMin = stack.Pop();
}
return ret;
}
}
Agregue un campo para mantener el valor mínimo y actualizarlo durante Pop () y Push (). De esa forma, getMinimum () será O (1), pero Pop () y Push () tendrán que trabajar un poco más.
Si se muestra el valor mínimo, Pop () será O (n); de lo contrario, ambos seguirán siendo O (1). Al cambiar el tamaño, Push () se convierte en O (n) según la implementación de Stack.
Aquí hay una implementación rápida
fuente
Almacena el mínimo actual explícitamente, y si el mínimo cambia, en lugar de presionar el valor, empuja un valor con la misma diferencia al otro lado del nuevo mínimo (si min = 7 y usted presiona 5, presiona 3 en su lugar (5- | 7-5 | = 3) y establece min en 5; si luego saca 3 cuando min es 5, ve que el valor emergente es menor que min, por lo que invierte el procedimiento para obtener 7 para el nuevo min, luego devuelve el anterior min). Como cualquier valor que no causa un cambio, el mínimo actual es mayor que el mínimo actual, tiene algo que puede usarse para diferenciar entre los valores que cambian el mínimo y los que no lo hacen.
En los lenguajes que usan números enteros de tamaño fijo, está tomando prestado un poco de espacio de la representación de los valores, por lo que puede subdesbordarse y la aserción fallará. Pero de lo contrario, es un espacio extra constante y todas las operaciones siguen siendo O (1).
Las pilas que se basan en cambio en listas vinculadas tienen otros lugares de los que puede tomar prestado un poco, por ejemplo, en C el bit menos significativo del siguiente puntero, o en Java el tipo de objetos en la lista vinculada. Para Java, esto significa que se usa más espacio en comparación con una pila contigua, ya que tiene la sobrecarga del objeto por enlace:
En C, la sobrecarga no está ahí, y puede tomar prestado el lsb del siguiente puntero:
Sin embargo, ninguno de estos es verdaderamente O (1). No requieren más espacio en la práctica, porque aprovechan los huecos en las representaciones de números, objetos o punteros en estos lenguajes. Pero una máquina teórica que usara una representación más compacta requeriría agregar un bit extra a esa representación en cada caso.
fuente
pop()
si el último valor enviado fueInteger.MIN_VALUE
(por ejemplo, push 1, push Integer.MIN_VALUE, pop). Esto se debe al desbordamiento como se mencionó anteriormente. De lo contrario, funciona para todos los valores enteros.Encontré una solución que satisface todas las restricciones mencionadas (operaciones de tiempo constante) y espacio extra constante .
La idea es almacenar la diferencia entre el valor mínimo y el número de entrada, y actualizar el valor mínimo si ya no es el mínimo.
El código es el siguiente:
El crédito va a: https://leetcode.com/discuss/15679/share-my-java-solution-with-only-one-stack
fuente
Bueno, ¿cuáles son las limitaciones de tiempo de ejecución de
push
ypop
? Si no se requiere que sean constantes, simplemente calcule el valor mínimo en esas dos operaciones (haciéndolas O ( n )). De lo contrario, no veo cómo se puede hacer esto con espacio adicional constante.fuente
Aquí está mi código que se ejecuta con O (1). El código anterior que publiqué tenía un problema cuando aparece el elemento mínimo. Modifiqué mi código. Este usa otra pila que mantiene el elemento mínimo presente en la pila por encima del elemento empujado actual.
fuente
Usé un tipo diferente de pila. Aquí está la implementación.
Salida:
Intentalo. Creo que responde a la pregunta. El segundo elemento de cada par da el valor mínimo visto cuando se insertó ese elemento.
fuente
Estoy publicando el código completo aquí para encontrar el mínimo y el máximo en una pila determinada.
La complejidad del tiempo será O (1) ..
Avísame si tienes algún problema
Gracias, Vikash
fuente
Puede extender su clase de pila original y simplemente agregarle el seguimiento mínimo. Deje que la clase padre original maneje todo lo demás como de costumbre.
fuente
Aquí está mi solución en java usando la lista de me gusta.
}
fuente
Supongamos que la pila en la que trabajaremos es la siguiente:
En la representación anterior, la pila solo se construye con el valor de la izquierda, el [valor mínimo] del valor de la derecha se escribe solo con fines ilustrativos, que se almacenará en una variable.
El problema real es cuando el valor que es el valor mínimo obtenido se elimina en ese punto, ¿cómo podemos saber cuál es el siguiente elemento mínimo sin iterar sobre la pila?
Como, por ejemplo, en nuestra pila cuando aparecen 6 get, sabemos que este no es el elemento mínimo porque el elemento mínimo es 2, por lo que podemos eliminarlo de forma segura sin actualizar nuestro valor mínimo.
Pero cuando mostramos 2, podemos ver que el valor mínimo es 2 en este momento y si este mensaje aparece, entonces debemos actualizar el valor mínimo a 3.
Punto 1:
Ahora, si observa cuidadosamente, necesitamos generar minvalue = 3 a partir de este estado particular [2, minvalue = 2]. o si va a depper en la pila, necesitamos generar minvalue = 7 a partir de este estado particular [3, minvalue = 3] o si va más depper en la pila, entonces necesitamos generar minvalue = 8 a partir de este estado en particular [7, minvalue = 7]
¿Notó algo en común en los 3 casos anteriores? El valor que necesitamos generar depende de dos variables que son iguales. Correcto. ¿Por qué sucede esto? Porque cuando empujamos algún elemento más pequeño que el minvalue actual, básicamente empujamos ese elemento en la pila y también actualizamos el mismo número en minvalue.
Punto2:
Básicamente, estamos almacenando duplicados del mismo número una vez en la pila y una vez en la variable minvalue. Necesitamos enfocarnos en evitar esta duplicación y almacenar algo de datos útiles en la pila o el minvalue para generar el mínimo anterior como se muestra en CASOS arriba.
Centrémonos en lo que debemos almacenar en la pila cuando el valor para almacenar en push es menor que el valor mínimo. Vamos a nombrar esta variable y, por lo que ahora nuestra pila se verá así:
Los he renombrado como y1, y2, y3 para evitar confusión de que todos tendrán el mismo valor.
Punto3:
Ahora intentemos encontrar algunas restricciones sobre y1, y2 e y3. ¿Recuerda cuándo exactamente necesitamos actualizar el minvalue mientras hacemos pop (), solo cuando hemos extraído el elemento que es igual al minvalue? Si sacamos algo mayor que el minvalue, entonces no tenemos que actualizar minvalue. Entonces, para activar la actualización de minvalue, y1, y2 & y3 deberían ser más pequeños que el minvalue correspondiente. [Estamos evitando la igualdad para evitar duplicar [Point2]] por lo que la restricción es [y <minValue].
Ahora volvamos a rellenar y, necesitamos generar algún valor y poner y en el momento de empujar, recuerda. Tomemos el valor que viene para empujar como x, que es menor que prevMinvalue, y el valor que realmente empujaremos en la pila es y. Así que una cosa es obvia que newMinValue = x, y y <newMinvalue.
Ahora necesitamos calcular y (recuerde que y puede ser cualquier número que sea menor que newMinValue (x) así que necesitamos encontrar algún número que pueda cumplir con nuestra restricción) con la ayuda de prevMinvalue yx (newMinvalue).
Entonces, en el momento de presionar x si es menor que prevMinvalue, presionamos y [2 * x-prevMinValue] y actualizamos newMinValue = x.
Y en el momento del estallido, si la pila contiene algo menos que minValue, ese es nuestro disparador para actualizar minVAlue. Tenemos que calcular prevMinValue a partir de curMinValue e y. y = 2 * curMinValue - prevMinValue [probado] prevMinVAlue = 2 * curMinvalue - y.
2 * curMinValue - y es el número que necesitamos actualizar ahora al prevMinValue.
El código para la misma lógica se comparte a continuación con la complejidad de O (1) tiempo y O (1) espacio.
fuente
Aquí está mi versión de implementación.
fuente
fuente
Encontré esta solución aquí
fuente
fuente
fuente
fuente
Aquí está mi código que se ejecuta con O (1). Aquí utilicé un par de vectores que contiene el valor que empujó y también contiene el valor mínimo hasta este valor empujado.
Aquí está mi versión de la implementación de C ++.
fuente
fuente
Class
-MinStack
? La documentación de Java de Oracle aconseja utilizarDeque
.Una implementación práctica para encontrar el mínimo en una pila de objetos diseñados por el usuario, denominada: Escuela
La pila va a almacenar las escuelas en la pila según el rango asignado a una escuela en una región específica, digamos, findMin () le da a la escuela donde obtenemos el número máximo de solicitudes de admisión, que a su vez se definirá por el comparador que utiliza el rango asociado con las escuelas en la temporada anterior.
Además, el objeto de la escuela es el siguiente:
Este ejemplo cubre lo siguiente: 1. Implementación de Stack para objetos definidos por el usuario, aquí, School 2. Implementación para el método hashcode () y equals () usando todos los campos de Objetos a comparar 3. Una implementación práctica para el escenario donde rqeuire para que la operación Stack contains esté en el orden de O (1)
fuente
language-agnostic
especifique lo que usa para el código (y elimine los espacios en blanco antesThe Code for same is below:
). ¿Cómo se admite estostack.pop()
? (push()
¿ y ?)Aquí está la implementación de PHP de lo que se explica en la respuesta de Jon Skeet como la implementación de complejidad de espacio ligeramente mejor para obtener el máximo de pila en O (1).
fuente
Aquí está la implementación de C ++ de Jon Skeets Answer . Puede que no sea la forma más óptima de implementarlo, pero hace exactamente lo que se supone que debe hacer.
Y aquí está el conductor de la clase.
Salida:
fuente
Nosotros podemos hacer esto en un tiempo O (n) y O (1) la complejidad del espacio, así:
fuente
Creo que simplemente puede usar una LinkedList en la implementación de su pila.
La primera vez que inserta un valor, coloca este valor como el encabezado de la lista vinculada.
luego, cada vez que presiona un valor, si el nuevo valor <head.data, realice una operación de envío previo (lo que significa que el cabezal se convierte en el nuevo valor)
si no es así, realice una operación de adición.
Cuando hace un pop (), comprueba si min == linkedlist.head.data, si es así, entonces head = head.next;
Aquí está mi código.
fuente
fuente
fuente
Vi una solución brillante aquí: https://www.geeksforgeeks.org/design-a-stack-that-supports-getmin-in-o1-time-and-o1-extra-space/
A continuación se muestra el código de Python que escribí siguiendo el algoritmo:
fuente
Para obtener elementos mínimos de Stack. Tenemos que usar Two stack .ie Stack s1 y Stack s2.
--------------------- Llame recursivamente del Paso 2 al 4 -----------------------
si se agregó un nuevo elemento a la pila s1.Entonces, saque los elementos de la pila s2
compare los nuevos elementos con s2. cuál es más pequeño, empuje a s2.
pop de la pila s2 (que contiene el elemento min)
El código se ve así:
fuente
Creo que solo la operación de empuje sufre, es suficiente. Mi implementación incluye una pila de nodos. Cada nodo contiene el elemento de datos y también el mínimo en ese momento. Este mínimo se actualiza cada vez que se realiza una operación de inserción.
Aquí hay algunos puntos para comprender:
Implementé la pila usando Linked List.
La parte superior de un puntero siempre apunta al último elemento empujado. Cuando no hay ningún elemento en esa pila, la parte superior es NULL.
Cuando se inserta un elemento, se asigna un nuevo nodo que tiene un puntero siguiente que apunta a la pila anterior y la parte superior se actualiza para apuntar a este nuevo nodo.
La única diferencia con la implementación de pila normal es que durante la inserción se actualiza un miembro mínimo para el nuevo nodo.
Eche un vistazo al código que se implementa en C ++ con fines de demostración.
Y la salida del programa se ve así:
fuente
fuente