tema
Título: dado el nodo principal de una lista enlazada individualmente, invierta el orden de los enlaces de la lista enlazada individualmente
Ejemplo:
Entrada: 1->2->3->4->5
Salida: 5->4->3->2 ->1
Requisitos: implementado de dos maneras
Solución
Método 1: (método de iteración directa)
tren de pensamiento
La estructura de una lista enlazada individualmente es la siguiente:
Como se muestra en la figura. Analicemos en qué debemos pensar si queremos completar el giro:
- Recorre todos los nodos hasta el final. Entonces necesitas confirmar la condición final.
- Apunte de forma segura el nodo actual a su nodo predecesor (apuntar directamente a él hará que falle el recorrido de la lista vinculada, como se muestra a continuación, no se puede encontrar el siguiente nodo de operación)
Para el primer punto, dado que desea realizar un bucle, no es más que un iterador o un bucle. Obviamente no hay forma de utilizar un iterador aquí, así que considere usar un bucle while
. ¿Cuando terminará? No es difícil encontrar que es [cuando el siguiente nodo del nodo actual es nulo], o agregamos un nuevo nodo currNode
para representar el nodo de operación actual, que se mueve hacia atrás con la profundidad transversal. Obviamente su valor inicial es head
el nodo principal, por lo que puede usarse currNode == null
para indicar el final del recorrido.
Para el punto 2, ¿cómo puede el nodo actual apuntar de forma segura a su nodo predecesor? Normalmente, se requieren los siguientes 2 pasos para garantizar:
- Registre el siguiente nodo señalador original del nodo actual, que se registra aquí
oldNextNode
(consulte la figura anterior para conocer el motivo) - También se necesita un nodo precursor
preNode
para registrar el nodo precursor que debe configurarse para el siguiente nodo (también puede ver la figura anterior, excepto que no se puede encontrar el siguiente nodo, de hecho, el siguiente nodo no puede encontrar el nodo anterior. por lo que también es necesario registrarlo)
Cuando estemos listos podemos probar la simulación.
Pasos:
- establecer
oldNextNode
encurrNode
el siguiente nodo - Señale
currNode
el siguiente nodo depreNode
preNode
Establecer comocurrNode
currNode
ConfigurarlooldNextNode
parece estar bien, así que comencemos a escribir código.
ejemplo de código
El código se muestra a continuación:
public class ReverseLinkedList {
public static void main(String[] args) {
ListNode node5 = new ListNode(5, null);
ListNode node4 = new ListNode(4, node5);
ListNode node3 = new ListNode(3, node4);
ListNode node2 = new ListNode(2, node3);
ListNode node1 = new ListNode(1, node2);
reverseLinkedList(node1);
}
private static void reverseLinkedList(ListNode head) {
// 初始化我们的中间节点
ListNode currNode = head;
ListNode oldNextNode = null;
ListNode preNode = null;
// 循环遍历,currNode != null 则表示还没遍历到结尾
while (currNode != null) {
oldNextNode = currNode.next;
currNode.next = preNode;
preNode = currNode;
currNode = oldNextNode;
}
System.out.println("试试看");
System.out.println(preNode);
}
}
Finalmente, veamos el resultado: ¡
está al revés!
Método 2: (método recursivo)
tren de pensamiento
(PD: dado que se dice que se resuelve mediante recursividad, primero debemos saber qué puede hacer la recursividad. Se puede decir que cualquier problema que pueda resolverse mediante recursividad debe poder dividir un problema grande en N problemas pequeños con puntos en común pregunta. Entonces, podemos mirar en esa dirección.)
Aún mirando el diagrama de estructura de la lista enlazada individualmente:
encontramos que si comenzamos desde el último nodo, el problema de este tema se convertirá en otro problema:¿Quién es mi nodo precursor?
En el recorrido normal, utilizamos preNode
nodos de estado para realizar un seguimiento de los nodos predecesores, ¡pero en la recursividad es menos complicado! Porque al atravesar nodos de forma recursiva hacia adelante, el final del nodo actual debe estar en el lugar de llamada del nodo predecesor. El diagrama de efectos es el siguiente:
el código recursivo es el siguiente:
public static void main(String[] args) {
ListNode node5 = new ListNode(5, null);
ListNode node4 = new ListNode(4, node5);
ListNode node3 = new ListNode(3, node4);
ListNode node2 = new ListNode(2, node3);
ListNode node1 = new ListNode(1, node2);
// reverseLinkedList(node1);
recursion(node1);
}
private static ListNode recursion(ListNode curr) {
// 递归出口(遍历到最后了)
if (curr == null || curr.next == null) {
return curr;
}
ListNode recursion = recursion(curr.next);
System.out.println(recursion.value);
return curr;
}
// 系统输出:
// 5
// 4
// 3
// 2
Ahora que conocemos esta característica, solo nos falta realizar los siguientes pasos:
- Cada llamada recursiva devuelve el nodo actual (esto es para crear un entorno donde el nodo actual pueda estar en el mismo bloque de código que el nodo predecesor)
- Después de obtener el nodo devuelto por la llamada recursiva
recursion
,recursion .next = curr; curr.next = null;
úselo
ejemplo de código
public static void main(String[] args) {
ListNode node5 = new ListNode(5, null);
ListNode node4 = new ListNode(4, node5);
ListNode node3 = new ListNode(3, node4);
ListNode node2 = new ListNode(2, node3);
ListNode node1 = new ListNode(1, node2);
// reverseLinkedList(node1);
recursion(node1);
System.out.println(node5);
}
private static ListNode recursion(ListNode curr) {
// 递归出口(遍历到最后了)
if (curr == null || curr.next == null) {
return curr;
}
ListNode recursion = recursion(curr.next);
recursion.next = curr;
curr.next = null;
return curr;
}
Finalmente, veamos el resultado: