Skip to content Skip to footer

Unraveling the Mysteries of Linked Lists in JavaScript

Hey, JavaScript aficionados! Today, we’re diving deep into the rabbit hole of data structures, and guess what’s on the menu? Linked Lists! These aren’t your everyday arrays; they’re a bit more nuanced, and knowing your way around them can seriously up your coding game.

What’s a Linked List Anyway?

In the simplest terms, a linked list is a collection of nodes, where each node contains data and a reference to the next node in the sequence. Imagine it like a treasure hunt, where each clue leads to the next, except here, each piece of data points to the next one.

Unlike arrays, linked lists don’t have indices. So, if you’re thinking about accessing the nth item in a jiffy, you might want to stick with arrays. But if you’re after insertions and deletions that don’t cause a ruckus in memory, linked lists are your go-to.

Crafting a Linked List from Scratch

Let’s get our hands dirty and build a linked list from the ground up. We’ll kick things off by defining a node:

class Node {
  constructor(data, next = null) {
    this.data = data;
    this.next = next;
  }
}

This Node class is pretty straightforward. It has a constructor that takes in some data and optionally the next node. If you don’t pass a next node, it defaults to null, which is our way of saying “this is the end of the line, folks!”

Next up, the LinkedList class:

class LinkedList {
  constructor() {
    this.head = null;
    this.size = 0;
  }

  // Add a node to the front of the list
  insertFirst(data) {
    this.head = new Node(data, this.head);
    this.size++;
  }

  // Add a node to the end of the list
  insertLast(data) {
    let node = new Node(data);
    let current;

    // If empty, make head
    if (!this.head) {
      this.head = node;
    } else {
      current = this.head;

      while (current.next) {
        current = current.next;
      }

      current.next = node;
    }

    this.size++;
  }

  // Insert at index
  insertAt(data, index) {
    // If index is out of range
    if (index > 0 && index > this.size) {
      return;
    }

    // If first index
    if (index === 0) {
      this.insertFirst(data);
      return;
    }

    const node = new Node(data);
    let current, previous;

    // Set current to first
    current = this.head;
    let count = 0;

    // Iterate to the right index
    while (count < index) {
      previous = current; // Node before index
      count++;
      current = current.next; // Node after index
    }

    node.next = current;
    previous.next = node;

    this.size++;
  }

  // Get at index
  getAt(index) {
    let current = this.head;
    let count = 0;

    while (current) {
      if (count == index) {
        console.log(current.data);
      }
      count++;
      current = current.next;
    }

    return null;
  }

  // ... More methods to come
}

With our LinkedList class, we’re setting the stage for all the action. We’ve got methods to insert nodes at the beginning (insertFirst), at the end (insertLast), and at any given index (insertAt). We’re also able to fetch data from a specific index with getAt.

Remember, linked lists are all about connections. When we insert at a specific index, we’re not just plopping a value down; we’re carefully re-wiring the .next references so everything stays linked.

Inserting and Retrieving Like a Pro

Let’s see our linked list in action. We’ll create a list and start adding some elements to it:

const ll = new LinkedList();
ll.insertFirst(100);
ll.insertFirst(200);
ll.insertLast(300);
ll.insertAt(500, 1);

ll.getAt(2); // Should log 300

Here we’re playing around with our list, adding elements to various positions and then retrieving one to see if it’s all working as expected.

Conclusion and What’s Next

Alright, code wranglers, we’ve just scratched the surface of linked lists in JavaScript. We’ve laid the foundation and started building up our list with some basic operations. But there’s more to it—deleting nodes, clearing the list, printing its contents, and even reversing it!

Stay tuned for the next installment where we’ll continue to explore these operations and really get to grips with the power of linked lists. Until then, keep those nodes linking and your JavaScript skills sharp!

Welcome back, fellow coders! Last time, we set up our very own linked list in JavaScript and learned how to add nodes like bosses. Now, let’s turn up the heat and tackle some more advanced maneuvers—deleting nodes, searching for values, and flipping our list on its head.

Deleting Nodes Without Breaking the Chain

Sometimes, you’ve got to let go of things… like nodes in a linked list. Here’s how we do it without causing a data structure meltdown:

// Remove at index
removeAt(index) {
  if (index > 0 && index > this.size) {
    return;
  }

  let current = this.head;
  let previous;
  let count = 0;

  // Remove first
  if (index === 0) {
    this.head = current.next;
  } else {
    while (count < index) {
      count++;
      previous = current;
      current = current.next;
    }

    previous.next = current.next;
  }

  this.size--;
}

This removeAt method is our go-to for eliminating a node at a specific spot. We carefully reassign the .next reference of the previous node to skip over the one we’re ditching. It’s like ghosting in the linked list world.

Clearing the Slate

Sometimes you just need a fresh start, and that means wiping your linked list clean:

// Clear the list
clearList() {
  this.head = null;
  this.size = 0;
}

Calling clearList is the equivalent of a memory amnesia spell—it sets the head to null and resets the size to 0. Poof! Your list is now as empty as a hermit’s address book.

Printing the List’s Contents

To show off our linked list or for some good ol’ debugging, we need a way to print out its contents:

// Print list data
printListData() {
  let current = this.head;

  while (current) {
    console.log(current.data);
    current = current.next;
  }
}

With printListData, we’re just taking a casual stroll through our list and logging each node’s data. It’s the linked list equivalent of a roll call.

Reversing the Linked List

Here’s a party trick—reversing your linked list! It’s not just for show; it can be pretty handy in certain algorithms:

// Reverse the list
reverseList() {
  let current = this.head;
  let prev = null;
  let next = null;

  while (current) {
    next = current.next;
    current.next = prev;
    prev = current;
    current = next;
  }

  this.head = prev;
}

This reverseList method is like a choreographed dance where each node swaps its .next reference to the previous one, and at the end, we crown a new head.

Searching for Value

Looking for something? Let’s implement a search function to find a node with a given value:

// Find a node with given data
find(data) {
  let current = this.head;

  while (current) {
    if (current.data === data) {
      return current;
    }
    current = current.next;
  }

  return null;
}

The find method is like a game of Marco Polo, but with data. We keep calling out until we get a response that matches the data we’re looking for.

Conclusion

And there you have it, the full lowdown on linked lists in JavaScript! We’ve built, manipulated, reversed, and even searched our way through this essential data structure. From here, the sky’s the limit. You can extend this basic structure to create doubly linked lists, circular linked lists, or even use it to understand more complex structures like trees and graphs.

Remember, the beauty of linked lists lies in their simplicity and the elegance of their node connections. They may not be the right choice for every scenario, but they’re a powerful tool in your coding arsenal.

Keep experimenting, keep coding, and most importantly, keep linking those nodes together. Happy coding!