Supongamos que tenemos un conjunto y cada miembro de D es un par de datos y claves. Queremos una estructura de datos que soporte las siguientes operaciones:
- Inserte en D ,
- Eliminar miembro , (no es necesario buscar para encontrar e , por ejemplo, e apunta a un miembro en D ),
- MostFrequent, que devuelve un miembro tal que e . k e y es una de las claves más frecuentes en D (tenga en cuenta que la clave más frecuente no necesita ser única).
¿Cuál sería una implementación eficiente de esta estructura de datos?
Mi solución es un montón para las teclas y sus frecuencias priorizadas por las frecuencias más una tabla hash donde la función hash asigna miembros con la misma tecla a la misma ranura en la tabla hash (con punteros de cada parte a la otra).
Esto puede dar para las dos primeras operaciones y Θ ( 1 ) para la tercera (peor tiempo de ejecución).
Me pregunto si hay una solución más eficiente. (¿O una solución más simple con la misma eficiencia?)
Respuestas:
En el modelo de cálculo basado en la comparación, puede implementar la cola de prioridad utilizando un montón de Fibonacci en lugar de un montón común. Esto le dará los siguientes límites: tiempo amortizado para insertar y O ( log n ) tiempo amortizado para operaciones de eliminación.O(1) O ( logn )
Si se aleja del modelo basado en la comparación y adopta el modelo RAM donde las claves se consideran cadenas binarias, cada una contenida en una o más palabras de máquina, puede implementar su cola prioritaria en . De hecho, puede lograr tanto para insertar como para eliminar operaciones O ( √o ( logn ) yO(1)tiempo para la operación findMin. Thorup demostró queO ( logIniciar sesiónnorte-------√) O (1)
Si podemos ordenar claves en el tiempo S ( n ) por clave, entonces podemos implementar una cola prioritaria que soporte find-min en tiempo constante y actualizaciones (insertar y eliminar) en tiempo S ( n ) .norte S( n ) S( n )
Ver M. Thorup. Equivalencia entre colas prioritarias y clasificación, 2002. en Proc. FOCS 2002
Como podemos ordenar en tiempo esperado y espacio lineal, como lo muestraO (n logIniciar sesiónnorte-------√)
Y. Han y M. Thorup. Clasificación de enteros en tiempo esperado y espacio lineal. en Proc. FOCS 2002O (n logIniciar sesiónnorte-------√)
El límite está probado.
fuente
Puedes hacer todo esto en tiempo amortizado esperado. El truco esencial es que no necesitamos toda la potencia de una cola prioritaria, ya que la frecuencia clave solo cambia en 1 durante cada inserción o eliminación.O ( 1 )
Mi solución a continuación es realmente su solución con una cola de prioridad "ineficiente" que funciona bien para este caso: una cola de prioridad máxima implementada como listas doblemente vinculadas de cubos de claves tiene O (1) insertMin, deleteMax, removeFromBucket, y aumentar la clave.
Mantenga una lista doblemente vinculada de Buckets, donde cada Bucket tiene un conjunto de claves hash no vacías (que llamaré Cohorte) y un número entero positivo (que llamaré ValCount). En un Bucket b, cada clave k en la Cohorte de b tiene el mismo número de valores únicos asociados en el conjunto que está manteniendo. Por ejemplo, si su conjunto tiene los pares (a, manzana), (a, aguacate), (b, plátano), (c, pepino), (d, fruta del dragón) donde las letras individuales son las claves y las frutas son los valores, entonces tendría dos Buckets: un Bucket tendría un ValCount de 2 y una Cohorte que constaría de una sola clave: a. El otro Bucket tendría un ValCount de 1 y una Cohorte que constaría de las tres teclas b, c y d.
ValCount debe mantener ordenada la lista doblemente vinculada de Bucket. Será importante que podamos encontrar el encabezado y la cola de la lista en el tiempo y que podamos empalmar en un nuevo Cubo en O (O ( 1 ) si conocemos a sus vecinos. Inimaginablemente, llamaré a la lista de Buckets la BucketList.O ( 1 )
Además de BucketList, necesitaremos un SetMap, que es un mapa de claves de asignación de hash a ValueBuckets. Un ValueBucket es un par que consiste en el ValueSet (un conjunto de valores hash no vacío) y un puntero no nulo a un Bucket. El ValueSet asociado con una clave k contiene todos los valores únicos asociados con k. El puntero del depósito asociado con un ValueSet tiene una Cohorte igual al tamaño del ValueSet. El Bucket asociado con una clave k en SetMap también está asociado con la clave k en BucketList.
En C ++:
Para encontrar un par clave-valor de frecuencia máxima, solo tenemos que mirar el encabezado de la lista de cubo, encontrar una clave en la cohorte, buscar esa clave en el SetMap y encontrar un valor en el ValueSet de su ValueBucket. (¡Uf!)
Insertar y eliminar pares clave-valor es más complicado.
Para insertar o eliminar un par clave-valor, primero lo insertamos o eliminamos en el SetMap Esto cambiará el tamaño del ValueSet, por lo que debemos modificar el Bucket asociado con la clave. Los únicos Buckets que tendremos que mirar para hacer este cambio serán los vecinos inmediatos del Bucket en el que solía estar la llave. Hay varios casos aquí, y probablemente no valga la pena explicarlos completamente, aunque estaría feliz para elaborar si todavía tienes problemas.
fuente
La peor complejidad del caso
Prueba
Que se establece con una combinación de:
y:
Referencia
Thorup, Mikkel. "Colas de prioridad de enteros con clave de disminución en tiempo constante y el problema de los caminos más cortos de una sola fuente". En las actas del trigésimo quinto simposio anual de ACM sobre teoría de la computación, 149-158. STOC '03. Nueva York, NY, EE. UU .: ACM, 2003.
fuente