Puede desplazar todos los elementos restantes y costar O(n). Usar push y shift no garantiza una queue eficiente.
Una opción sencilla para algoritmos de una sola ejecución es un array con índice de lectura:
TypeScript
const queue =[start];let head =0;while(head < queue.length){const current = queue[head];
head +=1;// Enqueue with queue.push(...)}
Las posiciones procesadas permanecen en el array hasta terminar. Es eficiente en tiempo, pero no reutiliza memoria durante una queue de larga duración.
classQueue<T>{privatereadonly values =newMap<number,T>();private head =0;private tail =0;getsize():number{returnthis.tail -this.head;}isEmpty():boolean{returnthis.size ===0;}enqueue(value:T):void{this.values.set(this.tail, value);this.tail +=1;}dequeue():T|undefined{if(this.isEmpty())returnundefined;const value =this.values.get(this.head);this.values.delete(this.head);this.head +=1;if(this.head ===this.tail){this.head =0;this.tail =0;}return value;}front():T|undefined{returnthis.values.get(this.head);}}
Las operaciones son promedio O(1) por depender de Map. Una implementación de alto rendimiento suele usar un circular buffer para mejor localidad de memoria.
Una deque general puede usar un circular array que duplica capacidad al llenarse. Cada resize cuesta O(n), pero inserciones siguen siendo O(1) amortizadas.
La implementación requiere mantener:
índice del primer elemento;
cantidad de elementos;
conversión de índice lógico a físico;
copia ordenada al crecer.
Es más compleja que usar una librería, pero ofrece memoria contigua y operaciones eficientes.
Mantiene elementos en orden creciente o decreciente. Cuando llega un valor que vuelve inútiles a candidatos del tope, los retira.
Ejemplo: siguiente elemento mayor.
TypeScript
functionnextGreater(values:readonlynumber[]):number[]{const result =Array(values.length).fill(-1);const stack:number[]=[];for(let index =0; index < values.length; index +=1){while(
stack.length >0&&
values[index]> values[stack.at(-1)!]){
result[stack.pop()!]= values[index];}
stack.push(index);}return result;}
La stack guarda índices cuyos siguientes mayores aún no se conocen, y sus valores quedan en orden decreciente.
Aunque hay un while dentro de un for, cada índice entra y sale como máximo una vez:
queue dequeue al inicio y enqueue al final en O(1) si conserva tail.
Evita resize y desplazamientos, pero cada nodo añade memoria y peor localidad de caché. Un array dinámico suele ser más rápido para stack; una linked list puede ser útil si se necesitan nodos estables o tamaños impredecibles.
¿Por qué el algoritmo de next greater es O(n) aunque tenga un while dentro del for?
Respuesta
Porque cada índice se inserta una vez y se retira como máximo una vez. El total de iteraciones del while a lo largo de toda la ejecución es O(n), no O(n) por cada vuelta externa.