Ejercicio 2.1

Ejercicio 2.1

de Fabricio Gabriel Techera Rosado -
Número de respuestas: 3

//Precondicion recibe una lista vacia(Lista l == NULL)
Lista enOrden(AB arb, Lista l) // que recibe un árbol a y retorna una lista con los elementos de a ordenados según la recorrida en orden de a
{
    if(arb != NULL)
    {
        l = enOrden(arb->izq);
        l = new nodoLista;
        l->elem = arb->elem;
        l->sig = NULL;
        l = l->sig;
        l = enOrden(arb->der);
    }
    return l;
}

Buenas tengo duda si l me va a retornar el inicio o el final de mi lista. y bueno tambien si el codigo es funcional ya que me parece un poco complicado la parte de retornar punteros en recursion.

En respuesta a Fabricio Gabriel Techera Rosado

Re: Ejercicio 2.1

de Sofia Tito Virgilio Rodriguez -

Hola Fabricio,

¿Notaste que tu función recibe una lista como parámetro, pero luego en las llamadas recursivas no le pasas dicho parámetro?
Además, al estar recibiendo una lista vacía, como lo especifica la precondición, ¿por qué no te convence declarla dentro de la función en lugar de recibirla como parámetro?.

En cuanto a la funcionalidad del código, hay un tema con el manejo de l, veamos el recorrido del puntero l a lo largo del código.

Primero, le asignamos el resultado de invocar a la función con arb->izq:

l = enOrden(arb->izq);

es decir, que si todo sale bien, entonces l está apuntando al primer elemento de la lista resultante de realizar un recorrido en orden del subárbol izquierdo de arb. Acá, hay que tener en cuenta, sin embargo, que el subárbol izquierdo de arb puede ser vacío, y por lo tanto, el resultado de la invocación podría ser NULL.

Después, al mismo l, le hacemos un new nodoLista:

l = new nodoLista;

que lo que hace es que ahora ese l pase a apuntar a un nodo nuevo en memoria, pero ¿qué pasó con la lista resultante de invocar a la función con el subárbol izquierdo? La única referencia que teníamos hacia dicha lista era l, y ahora l "apunta" a otro lado, por lo tanto esa referencia se perdió. Es decir, habría que usar un puntero diferente para mantener la referencia al resultado de la primer invocación.

Luego cargamos el nuevo nodo (apuntado por l) con el elemento que está en la raíz del árbol, y hacemos que l->sig sea NULL:

l->elem = arb->elem;
l->sig = NULL;

pero luego, en la instrucción siguiente, le asignamos a l el valor de l->sig:

l = l->sig;

por lo que l ahora pasa a valer NULL y perdimos entonces la única referencia que teníamos al nuevo nodo que contenía el elemento de la raíz del árbol.

Luego, asignamos a l el resultado de la invocación a la función con el subárbol derecho:

l = enOrden(arb->der);

por lo que ahora l pasa a apuntar al primer nodo de la lista resultante de dicha invocación, pero en el camino perdimos tanto al resultado de enOrden(arb->izq), como al nuevo nodo que contenía al elemento de la raíz.

Esto nos dice que nos convendría utilizar al menos 3 punteros de tipo lista diferentes

  • uno para guardar la lista resultante de invocar a la función con el subárbol izquierdo, por ejemplo Lista lizq = enOrden(arb->izq);
  • otro para crear y guardar el nodo que contiene al elemento de la raiz del árbol, por elemplo Lista raiz = new nodoLista; raiz->elem = arb->elem;
  • el último para guardar la lista resultante de invocar a la función con el subárbol derecho, por ejemplo Lista lder = enOrden(arb->der);
Y luego, teniendo en cuenta que las listas lizq y lder pueden ser vacías, y por lo tanto, valer NULL, hay que separar por casos y ver en cada caso cómo deberíamos concatenar ambas listas y el nodo que tiene el elemento de la raíz del árbol para obtener la lista que debemos retornar.

Teniendo siempre en cuenta que si yo muevo el único puntero que está referenciando a un cierto lugar, entonces perdí la única referencia que tenía hacia ese lugar, por lo que si era información que deseo mantener entonces tendría que moverme con un puntero auxiliar, o guardar esa información en un puntero auxiliar antes de mover el original, y si no es información que deseo mantener, entonces debo liberarla antes de perder la referencia a ella.

En cuanto a tu pregunta, dado lo que vimos, si el árbol no es vacío l va a retornar el resultado de invocar a enOrden con el subárbol derecho de arb, porque es el último valor que le fue asignado.

Es un poco difícil de explicar todo esto por acá (y de entender), así que cualquier duda que te genere no dudes en volver a consultar,

Intenta hacerlo de nuevo con esto en mente y lo vemos,

Saludos!
En respuesta a Sofia Tito Virgilio Rodriguez

Re: Ejercicio 2.1

de Fabricio Gabriel Techera Rosado -

Se entendio, al menos gran parte, intente realizarlo sin ninguna funcion auxiliar pero me parece muy complicado, no se si directamente la funcion no se puede realizar sin un auxiliar, me habia olvidado del pequeño detalle de cuando invocaba la recursion pasar ambos parametros, ya que intente de varias formas y me olvide claramente de evaluar los casos borde, gracias por la aclaracion


En respuesta a Fabricio Gabriel Techera Rosado

Re: Ejercicio 2.1

de Sofia Tito Virgilio Rodriguez -

¿Por qué no querés usar funciones auxiliares?

De todas formas, pensémoslo como un problema de recursión.

Tenemos resuelto el caso base, que sucede cuando al árbol es vacío y el valor a retornar es NULL.

Luego, podemos asumir que sabemos resolver el problema para instancias más cercanas al caso base, en este caso, implica asumir que si realizamos las invocaciones:

Lista lizq = enOrden(arb->izq);

Lista lder = enOrden(arb->der);

Entonces en lizq vamos a tener la lista resultante de recorrer el subárbol izquierdo de arb en orden,

y en lder vamos a tener la lista resultante de recorrer el subárbol derecho de arb en orden.

Una vez que tenemos estas dos listas, y que además guardamos el elemento de la raíz del arbol en un nodo nuevo:

Lista* nuevo = new nodoLista;

nuevo->elem = arb->elem;

¿Cómo tenemos que integrar estas 3 partes para formar la lista que debemos retornar?