Hace media década, estaba sentado en una clase de estructuras de datos donde el profesor ofrecía crédito adicional si alguien podía atravesar un árbol sin usar recursividad, una pila, cola, etc. (o cualquier otra estructura de datos similar) y solo algunos indicadores. Se me ocurrió lo que pensé que era una respuesta obvia a esa pregunta que finalmente fue aceptada por el profesor. Estaba sentado en una clase de matemática discreta con otro profesor en el mismo departamento, y él afirmó que era imposible atravesar un árbol sin recursión, una pila, cola, etc., y que mi solución no era válida.
Entonces, ¿es posible o imposible? ¿Por qué o por qué no?
Editar: para agregar algunas aclaraciones, implementé esto en un árbol binario que tenía tres elementos: los datos almacenados en cada nodo y punteros a dos hijos. Mi solución podría extenderse a árboles n-ary con solo unos pocos cambios.
Mi maestro de estructuras de datos no impuso ninguna restricción contra la mutación del árbol, y de hecho descubrí más tarde que su propia solución era usar los punteros de los niños para señalar el árbol en su camino hacia abajo. Mi discreto profesor de matemáticas dijo que cualquier mutación de un árbol significa que ya no es un árbol de acuerdo con la definición matemática de un árbol, su definición también excluiría cualquier puntero a los padres, lo que coincidiría con el caso donde lo resolví anteriormente.
fuente
Respuestas:
Una gran cantidad de investigación en esta área ha sido domo, motivado por el método de atravesar árboles "a bajo costo" y estructuras de listas generales en el contexto de la recolección de basura.
Un árbol binario enhebrado es una representación adaptada de árboles binarios donde se utilizan algunos punteros nulos para enlazar a nodos sucesores en el árbol. Esta información adicional se puede utilizar para atravesar un árbol sin pila. Sin embargo, es necesario un bit adicional por nodo para distinguir los subprocesos de los punteros secundarios. Wikipedia: Tree_traversal
Hasta donde yo sé, los árboles binarios implementados utilizando punteros de la manera habitual (puntero izquierdo y derecho por nodo) se pueden recorrer utilizando el método de hilos, en un método atribuido a Morris . Los punteros NIL se reutilizan temporalmente para enhebrar un camino de regreso a la raíz. La parte inteligente es que durante el recorrido uno puede distinguir los bordes originales de los enlaces de hilo temporales, utilizando la forma en que forman ciclos en el árbol).
Buena parte: sin estructura de datos adicional. Parte mala: engañando un poco, la pila está dentro del árbol de una manera inteligente. Muy inteligente.
Una prueba de la pila oculta se muestra en P. Mateti y R. Manghirmalani: Algoritmo transversal del árbol de Morris Reconsiderado DOI: 10.1016 / 0167-6423 (88) 90063-9
JM Morris: atravesar árboles binarios de manera simple y económica. IPL 9 (1979) 197-200 DOI: 10.1016 / 0020-0190 (79) 90068-1
Luego también está el escaneo de Lindstrom . Este método "rota" los tres punteros involucrados en cada nodo (padre y dos hijos). Si desea realizar algún algoritmo decente de pedido previo o posterior, necesita bits adicionales por nodo. Si solo desea visitar todos los nodos (tres veces, pero nunca sabe qué visita realiza), puede hacerlo sin los bits.
G. Lindstrom: Escaneo de estructuras de listas sin pilas o bits de etiquetas. IPL 2 (1973) 47-51. DOI: 10.1016 / 0020-0190 (73) 90012-4
Quizás la forma más directa es un método de Robson . Aquí la pila necesaria para el algoritmo clásico se enhebra a través de las hojas.
JM Robson: un algoritmo mejorado para atravesar árboles binarios sin pila auxiliar IPL 1 (1973) 149-152. 10.1016 / 0020-0190 (73) 90018-5
IPL = Cartas de procesamiento de información
fuente
fuente
Mi solución fue un primer recorrido de cría utilizando bucles for anidados para forzar el árbol con fuerza bruta. Esto no es eficiente de ninguna manera, y de hecho una estructura de datos recursiva como un árbol está pidiendo un recorrido recursivo, pero la pregunta no era si un árbol podría atravesarse de manera eficiente, sino si era posible.
Para los primeros niveles se vería así, como puede ver, el operador bit a bit en el pseudocódigo simplemente decide un giro a la izquierda o derecha en un árbol binario:
Para n-ary, tomaría i% (maxChildren ** j) / j para determinar qué ruta tomar entre 0 y maxChildren.
En cada nodo en n-ary también necesitaría verificar si el número de hijos es mayor que maxChildren y actualizarlo adecuadamente.
fuente
depth
excede el ancho deint
?i
depth
depth
i