Lista enlazada recursiva inversa
algorithm recursion (4)
Considera la lista:
1 -> 2 -> 3 -> 4 -> NULL
^ ^
| |
first rest
Donde el first
apunta al primer nodo y el resto apunta al nodo próximo al first
.
Como la lista no está vacía y la lista no contiene un nodo, hacemos una llamada recursiva para reverse
para revertir la lista apuntada por el rest
. Así es como se ve la lista después de revertir el resto de la lista:
1 -> 2 <- 3 <- 4
^ | ^
| NULL |
first rest
Como se ve, el rest
ahora apunta a la lista invertida que tiene 4
al principio y 2
al final de la lista. El siguiente puntero del nodo 2
es NULL
.
Ahora debemos agregar el primer nodo al final de la lista de descanso invertido. Para agregar algo al final de la lista, necesitamos tener acceso al último nodo de la lista. En este caso, debemos tener acceso al último nodo de la lista de descanso invertido. Mire el diagrama, first -> next
puntos a la lista de descanso invertido del último nodo. Por lo tanto, first -> next -> next
será el siguiente puntero del último nodo de la lista de descanso invertido. Ahora tenemos que hacer que apunte first
lo que hacemos:
first -> next -> next = first;
Después de este paso la lista se ve como:
1 <- 2 <- 3 <- 4
^ -> ^
| |
first rest
Ahora el next
campo del último nodo de la lista debe ser NULL
. Pero ahora no es el caso. El next
campo del último nodo (nodo 1
) apunta al nodo anterior (nodo 2
). Para arreglar esto hacemos:
first -> next = NULL;
Después de esto la lista se ve como:
NULL <- 1 <- 2 <- 3 <- 4
^ ^
| |
first rest
Como se ve, la lista ahora se invierte correctamente y el rest
apunta al encabezado de la lista invertida.
Necesitamos devolver el nuevo puntero de cabeza para que los cambios se reflejen en la función de llamada. Pero esta es una función void
y la head
se pasa como puntero doble, por lo que cambiar el valor de *head
hará que la función de llamada vea la cabeza cambiada:
*head = rest;
Estaba mirando el código de abajo de la biblioteca de stanford:
void recursiveReverse(struct node** head_ref)
{
struct node* first;
struct node* rest;
/* empty list */
if (*head_ref == NULL)
return;
/* suppose first = {1, 2, 3}, rest = {2, 3} */
first = *head_ref;
rest = first->next;
/* List has only one node */
if (rest == NULL)
return;
/* put the first element on the end of the list */
recursiveReverse(&rest);
first->next->next = first;
/* tricky step -- see the diagram */
first->next = NULL;
/* fix the head pointer */
*head_ref = rest;
}
Lo que no entiendo está en el último paso recursivo, por ejemplo, si la lista es 1-2-3-4 Ahora, para el último paso recursivo primero será 1 y el resto será 2. Por lo tanto, si establece * head_ref = rest .. Eso hace que el jefe de la lista 2? ¿Alguien puede explicar cómo después de invertir el encabezado de la lista se convierte en 4?
Dibuja un rastro de pila ...
Intial - {1,2,3,4}
Head - 1
Rest = 2,3,4
Recurse(2,3,4)
Head = 2
Rest = 3,4
Recurse(3,4)
Head = 3
Rest = 4
Recurse (4)
Head = 4
Rest = null //Base Case Reached!! Unwind.
So now we pick up
Recurse(3,4)
Head = 3
Rest = 4
// Return picks up here
first->next->next = first;
so list is:
3,4,3
// set head to null,
null ,4,3,
//Off with his head!
4,3
Return
Now we''re here
Recurse(2,3,4)
Head = 2
Rest = 3,4
Previous return leaves state as:
Head = 2 //But Head -> next is still 3! -- We haven''t changed that yet..
Rest = 4,3
Head->next is 3,
Head->next->next = 2 makes the list (actually a tree now)
4->3->2
^
|
2
And chop off the head leaving
4->3->2
and return.
Similarly, do the last step which will leave
4->3->2->1
^
|
1
and chop off the head, which removes the one.
El resto no es 2
, es 2 -> 3 -> 4
, que se invierte recursivamente. Después de eso , configuramos *head_ref
en rest
, que ahora está (¡invertido de forma recursiva!) 4 -> 3 -> 2
.
El punto importante aquí es que, si bien el first
y el rest
tienen el mismo tipo, es decir, node*
, son conceptualmente diferentes: los first
puntos apuntan a un solo elemento, mientras que el rest
apunta a una lista enlazada de elementos. Esta lista enlazada se invierte recursivamente antes de asignarse a *head_ref
.
Recientemente escribí un método recursivo para revertir una lista vinculada en ruby. Aquí está:
def reverse!( node_1 = @head, node_2 = @head.link )
unless node_2.link
node_2.link = node_1
@head = node_2
return node_1
else
return_node = reverse!(node_1.link, node_2.link)
return_node.link = node_1
node_1.link = nil
return node_1
end
return self
end