Estoy aprendiendo Haskell y como ejercicio estoy haciendo árboles binarios. Habiendo hecho un árbol binario regular, quiero adaptarlo para que sea auto equilibrado. Entonces:
- ¿Cuál es más eficiente?
- ¿Cuál es más fácil de implementar?
- ¿Cuál se usa con más frecuencia?
Pero crucialmente, ¿cuál me recomiendan?
Supongo que esto pertenece aquí porque está abierto a debate.
haskell
functional-programming
data-structures
binary-tree
dan_waterworth
fuente
fuente
Respuestas:
Le recomendaría que comience con un árbol Rojo-Negro o un árbol AVL .
El árbol rojo-negro es más rápido para insertar, pero el árbol AVL tiene una ligera ventaja para las búsquedas. El árbol AVL es probablemente un poco más fácil de implementar, pero no mucho en base a mi propia experiencia.
El árbol AVL asegura que el árbol esté equilibrado después de cada inserción o eliminación (ningún subárbol tiene un factor de equilibrio mayor que 1 / -1, mientras que el árbol Rojo-negro asegura que el árbol esté razonablemente equilibrado en cualquier momento.
fuente
Consideraría una alternativa si está bien con estructuras de datos aleatorias : Saltar listas .
Desde un punto de vista de alto nivel, es una estructura de árbol, excepto que no se implementa como un árbol sino como una lista con múltiples capas de enlaces.
Obtendrá O (log N) inserciones / búsquedas / eliminaciones, y no tendrá que lidiar con todos esos casos difíciles de reequilibrio.
Sin embargo, nunca he considerado implementarlos en un lenguaje funcional, y la página de wikipedia no muestra ninguno, por lo que puede no ser fácil (wrt a inmutabilidad)
fuente
Si desea comenzar con una estructura relativamente fácil (tanto los árboles AVL como los árboles rojo-negros son complicados), una opción es un treap, denominado como una combinación de "árbol" y "montón".
Cada nodo obtiene un valor de "prioridad", a menudo asignado aleatoriamente a medida que se crea el nodo. Los nodos se colocan en el árbol de modo que se respete el orden de las claves y se respete el orden de los valores de prioridad en forma de montón. La ordenación de tipo montón significa que ambos hijos de un padre tienen prioridades más bajas que el padre.
EDITAR eliminado "dentro de los valores clave" arriba: la prioridad y el orden de las claves se aplican juntos, por lo que la prioridad es importante incluso para las claves únicas.
Es una combinación interesante. Si las claves son únicas y las prioridades son únicas, existe una estructura de árbol única para cualquier conjunto de nodos. Aun así, las inserciones y eliminaciones son eficientes. Estrictamente hablando, el árbol puede estar desequilibrado hasta el punto en que es efectivamente una lista vinculada, pero esto es extremadamente improbable (como con los árboles binarios estándar), incluso en casos normales como claves insertadas en orden (a diferencia de los árboles binarios estándar).
fuente
Vago y difícil de responder. Las complejidades computacionales están bien definidas. Si eso es lo que quiere decir con eficiencia, no hay un debate real. De hecho, todos los buenos algoritmos vienen con pruebas y factores de complejidad.
Si quiere decir "tiempo de ejecución" o "uso de memoria", deberá comparar las implementaciones reales. Luego, el lenguaje, el tiempo de ejecución, el sistema operativo y otros factores entran en juego, lo que hace que la pregunta sea difícil de responder.
Vago y difícil de responder. Algunos algoritmos pueden parecerle complejos, pero triviales para mí.
Vago y difícil de responder. Primero está el "¿por quién?" parte de esto? ¿Haskell solo? ¿Qué pasa con C o C ++? En segundo lugar, está el problema del software propietario en el que no tenemos acceso a la fuente para realizar una encuesta.
Correcto. Dado que sus otros criterios no son muy útiles, esto es todo lo que obtendrá.
Puede obtener el origen de una gran cantidad de algoritmos de árbol. Si desea aprender algo, simplemente puede implementar cada uno que pueda encontrar. En lugar de pedir una "recomendación", simplemente recopile todos los algoritmos que pueda encontrar.
Aquí está la lista:
http://en.wikipedia.org/wiki/Self-balancing_binary_search_tree
Hay seis populares definidos. Comience con esos.
fuente
Si está interesado en los árboles Splay, hay una versión más simple de los que creo que fue descrita por primera vez en un artículo de Allen y Munroe. No tiene las mismas garantías de rendimiento, pero evita complicaciones al tratar con el reequilibrio "zig-zig" frente a "zig-zag".
Básicamente, al buscar (incluidas las búsquedas de un punto de inserción o nodo para eliminar), el nodo que encuentra se gira directamente hacia la raíz, de abajo hacia arriba (por ejemplo, cuando sale una función de búsqueda recursiva). En cada paso, selecciona una rotación única hacia la izquierda o hacia la derecha, dependiendo de si el niño al que desea subir otro paso hacia la raíz era el niño derecho o el izquierdo (si recuerdo mis direcciones de rotación correctamente, eso es, respectivamente).
Al igual que los árboles Splay, la idea es que los elementos a los que se haya accedido recientemente siempre estén cerca de la raíz del árbol, por lo que es rápido acceder nuevamente. Siendo más simple, estos árboles Allen-Munroe de rotación a raíz (lo que yo llamo, no sé el nombre oficial) pueden ser más rápidos, pero no tienen la misma garantía de rendimiento amortizada.
Una cosa: dado que esta estructura de datos, por definición, muta incluso para las operaciones de búsqueda, probablemente deba implementarse de forma monádica. IOW tal vez no sea una buena opción para la programación funcional.
fuente
Un árbol equilibrado muy simple es un árbol AA . Su invariante es más simple y, por lo tanto, más fácil de implementar. Debido a su simplicidad, su rendimiento sigue siendo bueno.
Como ejercicio avanzado, puede intentar utilizar GADT para implementar una de las variantes de árboles equilibrados cuya invariante se aplica mediante el tipo de sistema de tipos.
fuente