Domina la lógica de una linked list al implementar los métodos append y prepend para agregar nodos al final y al inicio. Conecta correctamente la cabeza, la cola y el puntero next para que la estructura crezca sin errores y valida la longitud paso a paso con pruebas simples.
¿Cómo agregar nodos al final con append?
Para que un nuevo nodo sea la nueva cola, la secuencia es directa: crear el nodo, enlazarlo con el tail.next y actualizar la cola. Así logras que el null final se reemplace por el nuevo nodo y la lista crezca de forma consistente.
classNode{constructor(value){this.value= value;this.next=null;}}classLinkedList{constructor(initialValue){const firstNode =newNode(initialValue);this.head= firstNode;this.tail= firstNode;this.length=1;}append(value){const newNode =newNode(value);this.tail.next= newNode;// enlaza el último nodo actual con el nuevothis.tail= newNode;// el nuevo nodo se convierte en la colathis.length++;returnthis;}}
¿Cuál es la lógica de tail.next y cola?
Crear el nuevo nodo con el value que recibes.
Asignar tail.next al nuevo nodo para colocarlo al final.
Actualizar tail al nuevo nodo para que sea la cola.
Incrementar length para reflejar el nuevo tamaño.
¿Cómo validar la estructura y la longitud?
Verificar que la estructura existe con una longitud inicial de uno.
Ejecutar append(2): la cabeza sigue siendo 1, la cola ahora es 2.
Ejecutar append(3): la cola ahora es 3 y la cabeza permanece en 1.
Observar el anidado: cada nodo tiene value y un next que apunta al siguiente o a null al final.
¿Cómo insertar al inicio con prepend?
Para agregar un nodo como nueva cabeza, primero lo conectas al inicio y luego actualizas la referencia de la cabeza. La clave: apuntar el newNode.next al head actual y después mover la cabeza al newNode.
classLinkedList{// ... mismo constructor y Node que arribaprepend(value){const newNode =newNode(value); newNode.next=this.head;// conecta el nuevo nodo con la cabeza actualthis.head= newNode;// el nuevo nodo se convierte en la cabezathis.length++;returnthis;}}
¿Qué cambia en la cabeza y los pointers?
El nuevo nodo apunta con next a la cabeza anterior.
La referencia head pasa a ser el nuevo nodo.
La cola no cambia si la lista ya tenía más de un nodo.
La longitud aumenta en uno.
¿Qué se observa al probar con cero como nueva cabeza?
Ejecutar prepend(0): la cabeza ahora es 0.
La cola se mantiene en 1 si la lista era [1] antes.
La longitud refleja el cambio: pasa a 2 tras el prepend.
¿Qué habilidades y keywords refuerzas con esta práctica?
Comprendes cómo modelar una linked list con una clase que mantiene referencias a head, tail y length, y cómo un nodo encapsula value y next. Practicas el manejo de pointers para insertar correctamente sin romper la estructura. Además, validas el estado tras cada operación con retornos tipo return this y pruebas en el navegador.
Diseño de métodos: append para el final y prepend para el inicio.
Manipulación de referencias: head, tail, next y su actualización ordenada.
Pensamiento visual: crear un mapa mental de cómo cambia el null al enlazar un nuevo nodo.
Verificación incremental: revisar longitud y anidado tras cada operación.
Preparación para el reto: método insert para colocar un nodo en medio, pensando primero la lógica en papel.
Si ya implementaste tu método, comparte tu solución y explica cómo llegaste a ella: así todos aprendemos más rápido.
Algo que me ayudo bastante a la hora de entender esta clase fue que estamos jugando bastante con la referencia de JavaScript. Recuerda mantener la idea de que no estamos guardando los valores de los objetos en sí, si no el lugar [referencia] de donde se encuentran en el memory heap.
El memory Heap es donde se guardan todos los objetos, debido a que los objetos necesitan una mayor cantidad de memoria a diferencia que los números, cadenas o boleanos, la desventaja es que el memory heap es lento por lo cual se necesita dar una ubicación de donde se está guardado. Si deseas saber más acerca de este tema puedes revisar este video creado por el profesor del curso de fundamentos de javascript
😉 Mi explicación del código según a lo que entendí
classMySinglyLinkedList{constructor(value){// creamos el inicio de nuestro SinglyLinkedListthis.head={ value,next:null,};// Aqui sucede la magia ✨// Todo lo que modifiquemos en los atributos de tail// se modificará en la estructura inicial// por la RERENCIA!this.tail=this.head;this.length=1;}append(value){// aquí estamos creando un nuevo nodoconst newNode =newNode(value);// Como mencionamos anteriormente// si modificamos la cola por la REFERENCIA// se modificará la estructura inicial! 🖊this.tail.next= newNode;// Pero aun tail sigue apuntando a la CABEZA// de la estructura inicial entonces es momento// de apuntar al nuevo nodo creado para que posteriormente// podamos agregar más nodos! 🚀this.tail= newNode;// Finalmente aumentamos el tamaño definido de// nuestra estructura 👨🔧this.length++;returnthis;}
💚 Cualquier correción o comentario es bienvenido!
Gracias, tu aporte me ayudó a entenderlo, luego hice mis propios apuntes, explicado a como entendí, por si les interesa, si hay algún error diganme por favor :)
classListNode{constructor(value){this.value= value;this.next=null;}}classSinglyLinkedList{constructor(value){this.head={value: value,next:null,};//tail y head apuntan a la misma direccion de memoria// y si cambia UNA PROPIEDAD de uno, cambiará en el otrothis.tail=this.head;this.length=1;}//insertar al final//Esto funciona ya que en el contructor hay una referencia//cambiamos UNA PROPIEDAD del tail entonces también cambia en el headappend(value){const newNode =newListNode(value);//creamos nueva referenciathis.tail.next= newNode;//el next del head y el next del tail es el mismothis.tail= newNode;//tail y el next de head tienen la misma referenciathis.length++;returnthis;/*
Entonces ahora tail y ese nodo que se agregó
el next del head es el next de ese nodo que se agregó
Ambos next estan referenciados, COMO AL INICIO
entonces cuando creemos otro nodo simplemente se repetirá el proceso
*/}}let singlyLinkedList =newSinglyLinkedList(1);
Primero muchismas gracias me ayudo un monton a entender porque se cambiaba el next del head porque la verdad andaba enrredado y segundo:
Rogelio, mas que el que cambiar el head y el tail es mas como si head y tail son el valor de una casilla entonces al igualarlo estos dos valores realmente son la misma casilla, por lo cual el modificar el uno o el otro realmente lo que haces es modificar lo que hay dentro de esa casilla, ya que no son objetos diferentes. No se si lo entendiste asi, pero me parecio que depronto era un poco mas claro verlo asi con casillas de cajas y cajas '^^
Creo que para esta clase es importante entender también que cuando generamos un objeto en JS y luego igualamos otra variable a la variable anterior lo que hacemos es que apuntamos desde las dos variables al mismo espacio en memoria, por eso cuando decimos:
this.head=newNode(value)this.tail=this.head
Cada vez que hagamos un cambio en this.tail ese cambio también va a afectar a this.head, por eso en el método append, cuando le decimos:
this.tail.next= newNode
A su vez también cambiamos el valor this.head.next, luego hacemos que this.tail apunte al espacio en memoria de newNode diciéndole:
this.tail= newNode
La próxima vez que cambiemos this.tail ya no va a afectar a this.head.
Gracias a esto es que podemos aplicar los métodos append y prepend de esta manera, esto lo podemos probar haciendo en el constructor de SinglyLinkedList lo siguiente:
De esa manera this.tail ya no estará apuntando al espacio en memoria que ocupa this.head sino que pasará a ocupar uno nuevo, y los cambios que se hagan en this.tail no afectaran a this.head por ende el método de append la primera vez que se llama no hará que mute this.head. Esto es un problema porque no es el comportamiento que queremos, pero es importante que entendamos que no siempre la mutabilidad en JS es mala (aunque la querramos evitar en la mayoría de los casos) y qué podemos sacarle provecho sabiéndola utilizar. La otra opción es evitar que los objetos muten y tener control sobre lo que ocurre tanto en this.tail como en this.head.
¡Me salvaste bro! Me estaba partiendo la cabeza intentando entender eso, y medio tuve mis sospechas de que this.tail.next, afecta a this.head.next. Pero no lo pude corroborar. Gracias por tu aporte.
Tu explicación me hizo entender, ésta clase me resultó complicada precisamente por eso. Porque al hacer this.tail = this.head estamos señalando el mismo espacio en memoria. Gracias crack.
Un poco de ayuda grafica, saludos :)
Al inicio:
Intermedio :
Realmente tu primer ejemplo es al final y no al inicio.
Cierto, errorcillo, saludos :)
Es simple, de hecho me recuerda mucho a los callbacks hell jajaja, simplemente se trata de crear un nuevo nodo y meterlo dentro del next del nodo que está actualmente como la cola.
.
Y para la cabeza, lo único que hicimos fue, agarrar la cabeza actual, que ya contenía toda nuestra lista de nodos hijos, y meterla en una nueva cabeza, de esa forma pudimos hacer un prepend ^^
La logica es parecida al callback hell, es ir anidando punteros a direcciones en memoria de cada uno de los nuevos nodos, esto a final queda una estractura de cuando anidamos callbacks.
.
Para practicar la logica de programacion y que se les sea mas facil implementar en el codigo lo que se nos pide, yo les recomiendo hacer algo de pseudo codigo.
Escribir literal el paso a paso
dibujar ayuda tambien
la tecnica del patito de hule suele funcionar.
En lo personal yo hago un dibujo y escribo lo que deberia de hacer paso a paso. Esa es la manera en que practico mi logica. Te la recomiendo quizás a ti tambein te sirva como me sirve a mi
Gracias Juan. Lo estoy practicando y es lo que más me ayuda a resolver la logica.
Reto insert:
Lo pensé con posiciones del 1 en adelante. 1 sería la primera posición de la lista.
TIP: Para comentar todas las líneas juntas:
simplemente aprietan Ctrl + K + C.
TIP: Para quitar el comment a todas las líneas juntas:
simplemente aprietan Ctrl + K + U.
Saludos!!
No lo conocía de esa forma, también se puede usar ctrl + } (para comentar y sacar el comentario es igual)
Hey gracias! Lo habia buscado desde hace tiempo!!
Si la singly list tiene muchos elementos, al agregar un nuevo head, todos se corren automáticamente un lugar hacia delante sin que tengamos que usar un for como con los arrays?
Acá no se tienen posiciones fijas dentro de la lista sino cada nodo apunta al siguiente, para insertar un elemento al inicio simplemente debes decirle que va ser el nuevo head y que debe apuntar al head anterior. El resto de posiciones no van a cambiar porque van a seguir apuntando al mismo siguiente.
Sí, lo que sucede es lo siguiente:
Primero creas el nuevo nodo que será tu nuevo head dentro de poco.
Ese nuevo nodo es un objeto con dos propiedades: value y next. Inicialmente, next = null.
El HEAD actual es un objeto de objetos (objeto anidado), y contiene TODOS los nodos actuales.
¿Cómo entonces enlazas el head actual con el nuevo nodo?
Con la propiedad NEXT del nuevo nodo. Si al inicio newNode.next = null; basta con ponerle como valor de newNode.next a todooo el objeto HEAD actual (que a su vez ya contiene los demás nodos.)
newNode.next = this.head
Y así ya enlazaste los dos.
Pero falta un paso. Si lo dejas así nomás. no estás modificando nada de la linkedlist original, aún no has incluido el nuevo nodo dentro de la estructura de datos. Es como si simplemente crearas un nodo cualquiera patito y solo le pongas un valor X a su propiedad next.
Para hacer que el nuevo nodo de verdad sea la nueva cabeza de tu linked list, debes especificarlo.
"ahora quiero que mi head sea igual a este nuevo nodo que tengo (que ya está enlazado con todos los demás nodos)"
Y esto se hace así: this.head = newNode
Hice este pequeño ejemplo visual para mostrar cómo va cambiando la Singly Link List a medida que agregamos nodos con append y prepend ✨.
buen dia, cuando se agrega un nuevo nodo en la lista con el metodo insert con un solo parametro, se genera un undefine como lo de la imagen.
insert(index, value){//add a node "outside" the list (=> invalid)if(index <0|| index >this.length){returnnull;}elseif(index ===0){//add a node to the beginning of the listreturnthis.prepend(value);}elseif(index ===this.length){//>=//add a node to the end of the listreturnthis.append(value)}else{const newNode =newNode(value);const firstPointer =this.getTheIndex(index -1);const holdingPointer = firstPointer.next; firstPointer.next= newNode; newNode.next= holdingPointer;this.length++;returnthis;}}getTheIndex(index){let counter =0;let currentNode =this.head;while(counter !== index){ currentNode = currentNode.next; counter++;}return currentNode;}
¿Cómo es posible que se agregue y no se sobreescriba tail cada vez que se llame a la function append?
De verdad le aplaudo a este profesor. De lo mejor que hay en platzi. Joya!
No dejen ir al gran Diego De Granda
Callback Hell puro.
Dejo la solución del reto anterior
append(value){const newNode =newNode(value)this.tail.next= newNode //Apunto al siguiente nodothis.tail= newNode//Dejo la cola con el valorthis.length++}
Si creen que es difícil hacerlo en js imaginense hacerlo en C por primera vez