Close Menu
    DevStackTipsDevStackTips
    • Home
    • News & Updates
      1. Tech & Work
      2. View All

      Top 10 Use Cases of Vibe Coding in Large-Scale Node.js Applications

      September 3, 2025

      Cloudsmith launches ML Model Registry to provide a single source of truth for AI models and datasets

      September 3, 2025

      Kong Acquires OpenMeter to Unlock AI and API Monetization for the Agentic Era

      September 3, 2025

      Microsoft Graph CLI to be retired

      September 2, 2025

      ‘Cronos: The New Dawn’ was by far my favorite experience at Gamescom 2025 — Bloober might have cooked an Xbox / PC horror masterpiece

      September 4, 2025

      ASUS built a desktop gaming PC around a mobile CPU — it’s an interesting, if flawed, idea

      September 4, 2025

      Hollow Knight: Silksong arrives on Xbox Game Pass this week — and Xbox’s September 1–7 lineup also packs in the horror. Here’s every new game.

      September 4, 2025

      The Xbox remaster that brought Gears to PlayStation just passed a huge milestone — “ending the console war” and proving the series still has serious pulling power

      September 4, 2025
    • Development
      1. Algorithms & Data Structures
      2. Artificial Intelligence
      3. Back-End Development
      4. Databases
      5. Front-End Development
      6. Libraries & Frameworks
      7. Machine Learning
      8. Security
      9. Software Engineering
      10. Tools & IDEs
      11. Web Design
      12. Web Development
      13. Web Security
      14. Programming Languages
        • PHP
        • JavaScript
      Featured

      Magento (Adobe Commerce) or Optimizely Configured Commerce: Which One to Choose

      September 4, 2025
      Recent

      Magento (Adobe Commerce) or Optimizely Configured Commerce: Which One to Choose

      September 4, 2025

      Updates from N|Solid Runtime: The Best Open-Source Node.js RT Just Got Better

      September 3, 2025

      Scale Your Business with AI-Powered Solutions Built for Singapore’s Digital Economy

      September 3, 2025
    • Operating Systems
      1. Windows
      2. Linux
      3. macOS
      Featured

      ‘Cronos: The New Dawn’ was by far my favorite experience at Gamescom 2025 — Bloober might have cooked an Xbox / PC horror masterpiece

      September 4, 2025
      Recent

      ‘Cronos: The New Dawn’ was by far my favorite experience at Gamescom 2025 — Bloober might have cooked an Xbox / PC horror masterpiece

      September 4, 2025

      ASUS built a desktop gaming PC around a mobile CPU — it’s an interesting, if flawed, idea

      September 4, 2025

      Hollow Knight: Silksong arrives on Xbox Game Pass this week — and Xbox’s September 1–7 lineup also packs in the horror. Here’s every new game.

      September 4, 2025
    • Learning Resources
      • Books
      • Cheatsheets
      • Tutorials & Guides
    Home»Development»How to Code Linked Lists with TypeScript: A Handbook for Developers

    How to Code Linked Lists with TypeScript: A Handbook for Developers

    June 2, 2025

    A linked list is a data structure where each item, called a node, contains data and a pointer to the next node.

    Unlike arrays, which store elements in contiguous memory, linked lists connect nodes that can be scattered across memory.

    In this hands-on tutorial, you’ll build linked lists from scratch in TypeScript, starting with a basic singly linked list and progressing to advanced variations like doubly linked lists and circular lists.

    Here’s What We’ll Cover

    1. Prerequisites

    2. Getting Started

    3. What are Linked Lists?

    4. What is a Singly Linked List?

      • How to Create a Generic Node Structure for a Singly Linked List

      • How to Implement a Singly Linked List

      • What is the head Pointer in a Linked List?

      • How to Prepend a Node in a Singly Linked List

      • How to Append a Node in a Singly Linked List

      • How to Delete the head of a Singly Linked List

      • How to Delete the Last Node of a Singly Linked List

      • How to Delete a Node from a Singly Linked List

      • How to Find a Node in a Singly Linked List

      • How to Insert a Node at a Specific Position in a Singly Linked List

      • How to Traverse a Singly Linked List

      • How to Test Your Singly Linked List

    5. What is a Doubly Linked List?

      • How to Create a Generic Node Structure for a Doubly Linked List

      • How to Implement a Doubly Linked List

      • How to Prepend a Node in a Doubly Linked List

      • How to Append a Node in a Doubly Linked List

      • How to Delete the Head of a Doubly Linked List

      • How to Delete the Last Node of a Doubly Linked List

      • How to Delete a Node from a Doubly Linked List

      • How to Find a Node in a Doubly Linked List

      • How to Traverse a Doubly Linked List

      • How to Insert a Node at a Specific Position in a Doubly Linked List

      • How to Test Your Doubly Linked List

    6. What is a Circular Linked List?

    7. What is a Circular Singly Linked List?

      • How to Create a Generic Node Structure for a Circular Singly Linked List

      • How to Implement a Circular Singly Linked List

      • How to Prepend a Node in a Circular Singly Linked List

      • How to Append a Node in a Circular Singly Linked List

      • How to Delete the Head of a Circular Singly Linked List

      • How to Delete the Last Node of a Circular Singly Linked List

      • How to Delete a Node from a Circular Singly Linked List

      • How to Find a Node in a Circular Singly Linked List

      • How to Traverse a Circular Singly Linked List

      • How to Insert a Node at a Specific Position in a Circular Singly Linked List

      • How to Test Your Circular Singly Linked List

    8. What is a Circular Doubly Linked List?

      • How to Create a Generic Node Structure for a Circular Doubly Linked List

      • How to Implement a Circular Doubly Linked List

      • How to Prepend a Node in a Circular Doubly Linked List

      • How to Append a Node in a Circular Doubly Linked List

      • How to Delete the Last Node of a Circular Doubly Linked List

      • How to Delete the Head of a Circular Doubly Linked List

      • How to Find a Node in a Circular Doubly Linked List

      • How to Traverse a Circular Doubly Linked List

      • How to Delete a Node from a Circular Doubly Linked List

      • How to Insert a Node at a Specific Position in a Circular Doubly Linked List

      • How to Test Your Circular Doubly Linked List

    9. When to Use Linked Lists (and When to Avoid Them)

      • Why Use Linked Lists?

      • Real-World Use Cases

      • When Not to Use Linked Lists

      • Better Alternatives for Specific Cases

    10. Conclusion

    Prerequisites

    1. TypeScript: You need to know TypeScript basics, such as interfaces, types, and classes.

    2. Algorithm fundamentals: You need a basic understanding of data structures and algorithms. For example, you should be comfortable analyzing time and space complexity using Big-O notation.

    Getting Started

    To get started with this tutorial, you’ll use a playground project designed to help you implement linked lists and follow each step hands-on.

    Clone the project from the GitHub repository and code along with the tutorial.

    The project structure is as follows:

    fcc-linked-list/
    ├── src/
    │   ├── examples/
    │   │   ├── circular-1.ts
    │   │   ├── circular-2.ts
    │   │   ├── doubly.ts
    │   │   └── singly.ts
    │   └── playground/
    │       ├── circular-1.ts
    │       ├── circular-2.ts
    │       ├── doubly.ts
    │       └── singly.ts
    

    The examples directory contains the final version of each implementation. If you get stuck, you can look at these solutions as a last resort!

    What are Linked Lists?

    A linked list is a collection of elements called nodes, where each node contains data and a pointer to the next node, with the last node’s pointer typically pointing to null to mark the end of the list.

    Some linked lists have extra pointers to speed up changes anywhere in the list. But finding a node can be slow because you have to follow each pointer one by one and can’t jump directly to a node.

    Linked lists are the foundation for data structures like queues and stacks. The linked lists you create in this tutorial will support many other data structures.

    While linked lists can perform many operations, this tutorial will focus on the following:

    • prepend: adds a node to the beginning of the list.

    • append: adds a node to the end of the list.

    • delete: removes a specific node from the list.

    • deleteTail: removes the last node in the list.

    • deleteHead: removes the first node in the list.

    • insertAt: inserts a node at a specific position.

    • find: searches for and returns a node in the list.

    • traverse: visits each node in the list, usually from head to tail, for reading or processing data.

    Once you understand these basic operations, you’ll be able to implement any operation on your linked lists.

    Now that you understand the concept, let’s move to the next section and create your first linked list.

    What is a Singly Linked List?

    In this first section, you’ll create the simplest type of linked list, called a Singly Linked List.

    It’s called “Singly Linked” because each node points to only one other node, which is the next one in the list.

    Diagram of a singly linked list with four nodes labeled A, B, C, and D. It starts with the head at Node A and ends with the tail at Node D, pointing to NULL. Each node points to the next node in sequence.

    How to Create a Generic Node Structure for a Singly Linked List

    To start building a singly linked list, you need a Node structure that holds two main parts:

    • data: Stores the node’s value.

    • Next pointer: Links to the next node in the list or null if there’s no next node.

    Open src/playground/singly.ts, where you’ll find a class named N. Change it to the following code to set up the node structure:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    <span class="hljs-keyword">class</span> N<T> {
      <span class="hljs-comment">/** Node value */</span>
      <span class="hljs-keyword">public</span> data: T;
      <span class="hljs-comment">/** Next node reference */</span>
      <span class="hljs-keyword">public</span> next: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Creates a node with given value */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">value: T</span>) {
        <span class="hljs-built_in">this</span>.data = value;
      }
    }
    

    Here’s how the node structure works:

    1. Builds a generic Node: Uses <T> to handle any data type.

    2. Stores the node’s value: Assigns the value to the data property.

    3. Link to the next node: Sets the next pointer to the next node or null if there isn’t one.

    4. Initializes the node: Takes a value in the constructor and assigns it to data.

    Now, you can use the N class to create nodes in your singly linked list.

    How to Implement a Singly Linked List

    Let’s use the Node class you just created to build your singly linked list.

    Open src/playground/singly.ts where you’ll find the SinglyLinkedList class with a head pointer and several methods:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    <span class="hljs-keyword">class</span> N<T> {
      <span class="hljs-comment">/** Node value */</span>
      <span class="hljs-keyword">public</span> data: T;
      <span class="hljs-comment">/** Next node reference */</span>
      <span class="hljs-keyword">public</span> next: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Creates a node with given value */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">value: T</span>) {
        <span class="hljs-built_in">this</span>.data = value;
      }
    }
    
    <span class="hljs-comment">/** Singly linked list implementation */</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> SinglyLinkedList<T> {
      <span class="hljs-comment">/** Head node */</span>
      <span class="hljs-keyword">public</span> head: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Adds node to list start */</span>
      prepend(val: T): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Adds node to list end */</span>
      append(data: T): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Removes head node */</span>
      deleteHead(): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Removes tail node */</span>
      deleteTail(): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Removes first node with given value */</span>
      <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Finds node with given value */</span>
      find(data: T): N<T> | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Logs all node values */</span>
      traverse(): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Inserts node at given position */</span>
      insertAt(pos: <span class="hljs-built_in">number</span>, data: T): <span class="hljs-built_in">void</span> {}
    }
    

    By the end of this section, you’ll create each of these methods. But first, let’s discuss the head pointer.

    What is the head Pointer in a Linked List?

    The head is the first node in the list, and you begin from head when going through the list.

    You follow each node’s next pointer until you get to the last node, where next is null.

    If head is null, the list is empty.

    A non-empty singly linked list needs a head to be valid, or it’s broken.

    With this knowledge, you’re ready to start working on the operations.

    How to Prepend a Node in a Singly Linked List

    The goal is to add a new node to the beginning of your singly linked list and update the head pointer to this new node.

    Modify the prepend method in your SinglyLinkedList class in src/playground/singly.ts:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    prepend(data: T): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">const</span> newNode = <span class="hljs-keyword">new</span> N(data);
      newNode.next = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-built_in">this</span>.head = newNode;
    }
    

    The data prop holds the value for the new node. Here’s how prepend works:

    • Creates a new node with the given data.

    • Points the new node’s next to the current head.

    • Sets the head to the new node.

    Now, the head points to the new node, and the previous head is the second node in the list.

    This runs in O(1) time because you only change a few pointers without looping.

    How to Append a Node in a Singly Linked List

    The goal is to add a new node to the end of your singly linked list.

    Change the append method in your SinglyLinkedList class:

    <span class="hljs-comment">// src/playground/singly.ts</span>
    
    append(data: T): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">const</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = newNode;
        <span class="hljs-keyword">return</span>;
      }
    
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">while</span> (current.next) {
        current = current.next;
      }
    
      current.next = newNode;
    }
    

    To add a new node to the end of the list, you first need to find the last node. In a non-empty singly linked list, the last node’s next pointer always points to null.

    In other words, to find the last node in a non-empty singly linked list, look for the node whose next pointer is null.

    To append a new node, you should follow these steps:

    • Create a new node with the given value.

    • Check if the head is null. If it is, the list is empty, so set the head to the new node.

    • If the list has nodes, find the last node by looping through the list.

    • Start with a new pointer called current at the head.

    • Keep moving current to the next node until you hit a node with no next node (where next is null).

    • Link the last node’s next to the new node.

    Now, the new node is the last node, and its next points to null.

    This runs in O(n) time because you may need to traverse the entire list to find the last node.

    How to Delete the head of a Singly Linked List

    The goal is to delete the head of the list. Go ahead and update the deleteHead method in your SinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    deleteHead(): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-built_in">this</span>.head.next;
      }
    }
    

    You just need to move the head pointer to the second node in the list. The second node is head.next, so all you have to do is set head to head.next.

    And just like that, the old head is deleted!

    How to Delete the Last Node of a Singly Linked List

    The goal is to delete the last node, called the tail, from your singly linked list.

    The tail is the node whose next pointer is null.

    Modify the deleteTail method in your SinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    deleteTail(): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span>;
    
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head.next) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">return</span>;
      }
    
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">while</span> (current.next && current.next.next) {
        current = current.next;
      }
      current.next = <span class="hljs-literal">null</span>;
    }
    

    Here’s how deleteTail works:

    1. If the list is empty, it stops the operation because there is no tail to remove.

    2. If the head’s next is null, the list has only one node, which serves as both the head and the tail. To empty the list, simply set the head to null.

    3. If the list has more than one node, it finds the node right before the tail. It starts with a pointer called current at the head.

    4. It moves current forward until its next node is the tail. Then, it sets the next pointer of this node to null to make it the tail.

    5. Now, the last node is removed, and the new tail’s next points to null.

    This runs in O(n) time because you may need to traverse the entire list to find the node before the tail.

    How to Delete a Node from a Singly Linked List

    The goal is to remove the first occurrence of a node with a given value from your singly linked list.

    Let’s start by changing the delete method in your SinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span>;
    
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.data === data) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-built_in">this</span>.head.next;
        <span class="hljs-keyword">return</span>;
      }
    
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">while</span> (current.next) {
        <span class="hljs-keyword">if</span> (current.next.data === data) {
          current.next = current.next.next;
          <span class="hljs-keyword">return</span>;
        }
        current = current.next;
      }
    }
    

    Here’s how delete works:

    • The data prop is the value to find and remove

    • If the list is empty, it stops the operation because there is nothing to delete.

    • It checks if the head node’s value matches data prop. If it does, you don’t need to search further because the head is the node to delete, so it moves the head to head.next to remove the old head.

    • If the head doesn’t match, it creates a new pointer called current starting at the head.

    • It moves current through the list as long as there is a next node, and checks if the next node’s value matches the data prop.

    • If a match is found, it removes the next node by connecting current’s next to the node after it.

    • This takes the matched node out of the list because current now points to the node after the one you want to remove.

    • If no match is found, it keeps moving current to the next node until the end.

    This runs in O(n) time because you may need to traverse the entire list to find the node.

    How to Find a Node in a Singly Linked List

    The goal is to find the first occurrence of a node with the given value.

    Modify the find method inside the SinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    find(data: T): N<T> | <span class="hljs-literal">null</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">let</span> current: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">while</span> (current) {
        <span class="hljs-keyword">if</span> (current.data === data) <span class="hljs-keyword">return</span> current;
        current = current.next;
      }
    
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }
    

    The data prop is the value you’re searching for.

    Here’s how find works:

    • If the head is null, it returns null because the list is empty and there are no nodes to find.

    • It creates a new pointer called current at the head.

    • It moves current through the list while it exists and checks if its value matches data.

    • If a match is found, it returns the current node because it holds the value you’re looking for.

    • If no match is found, it moves current to the next node and keeps checking until the end.

    • If you reach the end without a match, it returns null.

    This runs in O(n) time because you may need to traverse the entire list to find the node.

    How to Insert a Node at a Specific Position in a Singly Linked List

    The goal is to add a new node at a specific position in your singly linked list.

    Modify the insertAt method in your SinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    insertAt(pos: <span class="hljs-built_in">number</span>, data: T): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">const</span> newNode = <span class="hljs-keyword">new</span> N(data);
      <span class="hljs-keyword">let</span> current: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">if</span> (pos < <span class="hljs-number">0</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"failed"</span>);
    
      <span class="hljs-keyword">if</span> (pos === <span class="hljs-number">0</span>) {
        newNode.next = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-built_in">this</span>.head = newNode;
        <span class="hljs-keyword">return</span>;
      }
    
      <span class="hljs-keyword">let</span> idx = <span class="hljs-number">0</span>;
    
      <span class="hljs-keyword">while</span> (current && idx < pos - <span class="hljs-number">1</span>) {
        current = current.next;
        idx++;
      }
    
      <span class="hljs-keyword">if</span> (!current) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"failed"</span>);
    
      newNode.next = current.next;
      current.next = newNode;
    }
    

    The pos prop is the position in the list, and data is the value.

    Here’s how insertAt works:

    • It creates a new node with the given value.

    • If the pos is negative, it stops the operation with an error because it’s not valid.

    • If pos is 0, it inserts the node at the head. It links the new node’s next to the current head, makes the new node the head, and stops the operation.

    • If the position is not 0, then it creates a pointer called current at the head and a counter called idx at 0.

    • It moves current through the list until you reach the node just before the desired position, increasing idx as you go.

    • If you reach the end of the list or the position is too large, it stops with an error.

    • It links the new node’s next to the node that is currently after the current node.

    • It links current’s next to the new node to insert it in the list.

    This runs in O(n) time because you may need to traverse the list to find the insertion point.

    How to Traverse a Singly Linked List

    The goal is to log the values of all nodes in your singly linked list.

    Modify the traverse method inside the SinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    traverse(): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-keyword">while</span> (current) {
        <span class="hljs-built_in">console</span>.log(current.data);
        current = current.next;
      }
    }
    

    Here’s how traverse works:

    • It starts by setting the current pointer to head to begin at the start of the list. If head is null, the list is empty.

    • If there are nodes in the list, it uses a while (current) loop to visit each one. In each loop, it logs current.data to display the node’s value, then moves the current pointer to current.next to go to the next node.

    • This loop continues until current becomes null, meaning you’ve reached the end of the list.

    Overall, the time complexity is O(n) due to traversing the whole list.

    How to Test Your Singly Linked List

    Congratulations! You’ve successfully created your singly linked list.

    Here’s what the final code should look like:

    <span class="hljs-comment">// 📁 src/playground/singly.ts</span>
    
    <span class="hljs-comment">/** Node for singly linked list */</span>
    <span class="hljs-keyword">class</span> N<T> {
      <span class="hljs-comment">/** Node value */</span>
      <span class="hljs-keyword">public</span> data: T;
      <span class="hljs-comment">/** Next node reference */</span>
      <span class="hljs-keyword">public</span> next: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Creates a node with given value */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">value: T</span>) {
        <span class="hljs-built_in">this</span>.data = value;
      }
    }
    
    <span class="hljs-comment">/** Singly linked list implementation */</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> SinglyLinkedList<T> {
      <span class="hljs-comment">/** Head node */</span>
      <span class="hljs-keyword">public</span> head: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Adds node to list start */</span>
      prepend(data: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">const</span> newNode = <span class="hljs-keyword">new</span> N(data);
        newNode.next = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-built_in">this</span>.head = newNode;
      }
    
      <span class="hljs-comment">/** Adds node to list end */</span>
      append(data: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">const</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = newNode;
          <span class="hljs-keyword">return</span>;
        }
    
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">while</span> (current.next) {
          current = current.next;
        }
    
        current.next = newNode;
      }
    
      <span class="hljs-comment">/** Removes head node */</span>
      deleteHead(): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-built_in">this</span>.head.next;
        }
      }
    
      <span class="hljs-comment">/** Removes tail node */</span>
      deleteTail(): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span>;
    
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head.next) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
          <span class="hljs-keyword">return</span>;
        }
    
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">while</span> (current.next && current.next.next) {
          current = current.next;
        }
    
        current.next = <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Removes first node with given value */</span>
      <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span>;
    
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.data === data) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-built_in">this</span>.head.next;
          <span class="hljs-keyword">return</span>;
        }
    
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">while</span> (current.next) {
          <span class="hljs-keyword">if</span> (current.next.data === data) {
            current.next = current.next.next;
            <span class="hljs-keyword">return</span>;
          }
    
          current = current.next;
        }
      }
    
      <span class="hljs-comment">/** Finds node with given value */</span>
      find(data: T): N<T> | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
        <span class="hljs-keyword">let</span> current: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">while</span> (current) {
          <span class="hljs-keyword">if</span> (current.data === data) <span class="hljs-keyword">return</span> current;
          current = current.next;
        }
    
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Logs all node values */</span>
      traverse(): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">while</span> (current) {
          <span class="hljs-built_in">console</span>.log(current.data);
          current = current.next;
        }
      }
    
      <span class="hljs-comment">/** Inserts node at given position */</span>
      insertAt(pos: <span class="hljs-built_in">number</span>, data: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">const</span> newNode = <span class="hljs-keyword">new</span> N(data);
        <span class="hljs-keyword">let</span> current: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">if</span> (pos < <span class="hljs-number">0</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"failed"</span>);
    
        <span class="hljs-keyword">if</span> (pos === <span class="hljs-number">0</span>) {
          newNode.next = <span class="hljs-built_in">this</span>.head;
          <span class="hljs-built_in">this</span>.head = newNode;
          <span class="hljs-keyword">return</span>;
        }
    
        <span class="hljs-keyword">let</span> idx = <span class="hljs-number">0</span>;
    
        <span class="hljs-keyword">while</span> (current && idx < pos - <span class="hljs-number">1</span>) {
          current = current.next;
          idx++;
        }
    
        <span class="hljs-keyword">if</span> (!current) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"failed"</span>);
    
        newNode.next = current.next;
        current.next = newNode;
      }
    }
    

    After you finish the implementation, run the following command to test your singly linked list:

    npm run <span class="hljs-built_in">test</span>:file singly
    

    If any tests fail, you can use src/examples/singly.ts to find and fix the issue, and then run the tests again.

    That’s it! You’ve successfully built a linked list and learned how to create nodes that point to the next node and perform operations on them.

    While singly linked lists are useful, they have a big limitation: each node only points to the next node.

    Wouldn’t it be great if nodes could also point to the previous node? This would let us do many more operations with our linked list.

    That’s exactly what you will learn in the next section.

    What is a Doubly Linked List?

    In this section, you’ll create a Doubly Linked List. It’s called “Doubly Linked” because each node points to both the next and previous nodes in the list.

    Diagram of a doubly linked list with nodes labeled A to D. Arrows indicate the "next" and "prev" connections between nodes, with Node A as the head and Node D as the tail. The tail points to NULL.

    First, let’s look at the node structure in a doubly linked list, and then you’ll implement the operations in the actual linked list.

    How to Create a Generic Node Structure for a Doubly Linked List

    The node structure in doubly linked lists is similar to singly linked lists, except it has an additional pointer to point to the previous node.

    The Node structure in a doubly linked list consists of three parts:

    • data: Stores the node’s value.

    • Next pointer: Links to the next node in the list or null if there’s no next node.

    • Previous pointer: Links to the previous node in the list or null if there’s no previous node.

    Open src/playground/doubly.ts and modify the N class with the following code to set up the node structure:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> N<T> {
      data: T;
      next: N<T> | <span class="hljs-literal">null</span>;
      prev: N<T> | <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">data: T</span>) {
        <span class="hljs-built_in">this</span>.data = data;
        <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.prev = <span class="hljs-literal">null</span>;
      }
    }
    

    Here’s how the node structure works:

    • It builds a generic Node: Uses <T> to handle any data type, such as numbers or strings.

    • It stores the node’s value: Assigns the value to the data property.

    • It links to the next node: Sets the next pointer to the next node or null if there isn’t one.

    • It links to the previous node: Sets the prev pointer to the previous node or null if there isn’t one.

    Then, the constructor sets the data when you create a new node.

    How to Implement a Doubly Linked List

    Now that the Node class is ready, you can start building the actual list.

    Open src/playground/doubly.ts and take a look at the DoublyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> N<T> {
      data: T;
      next: N<T> | <span class="hljs-literal">null</span>;
      prev: N<T> | <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">data: T</span>) {
        <span class="hljs-built_in">this</span>.data = data;
        <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.prev = <span class="hljs-literal">null</span>;
      }
    }
    
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> DoublyLinkedList<T> {
      <span class="hljs-comment">/** Head node */</span>
      <span class="hljs-keyword">public</span> head: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** Tail node */</span>
      <span class="hljs-keyword">public</span> tail: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** List length */</span>
      <span class="hljs-keyword">public</span> len: <span class="hljs-built_in">number</span>;
    
      <span class="hljs-comment">/** Creates an empty list */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.len = <span class="hljs-number">0</span>;
      }
    
      <span class="hljs-comment">/** Adds node to list start */</span>
      prepend(data: T): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Adds node to list end */</span>
      append(data: T): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Removes and returns head node data */</span>
      deleteHead(): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Removes and returns tail node data */</span>
      deleteTail(): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Removes first node with given data */</span>
      <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    
      <span class="hljs-comment">/** Finds node at given index */</span>
      find(idx: <span class="hljs-built_in">number</span>): N<T> | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Returns array of node data */</span>
      traverse(dir: <span class="hljs-string">"forward"</span> | <span class="hljs-string">"backward"</span> = <span class="hljs-string">"forward"</span>): T[] {
        <span class="hljs-keyword">return</span> [];
      }
    
      <span class="hljs-comment">/** Inserts node at given index */</span>
      insertAt(idx: <span class="hljs-built_in">number</span>, data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    }
    

    This class has two pointers, head and tail, and a variable called len:

    • head: This always points to the first item in your list.

    • tail: This always points to the last item in your list.

    • len: This represents the length of your linked list. Each time you modify the list by adding or removing a node, you need to update len to reflect the correct length.

    A valid doubly linked list will always have a head and a tail. If either the head or tail is null, it means the list is empty and has no nodes.

    That’s why you initially set the head and tail to null. When you create a list, it’s empty at first. As you add a node to the list, you update the pointers to point to the new node.

    Now, let’s move on to the next section and see how you can add a node to your doubly linked list.

    How to Prepend a Node in a Doubly Linked List

    The goal is to add a new node to the beginning of your doubly linked list and update the head pointer to this new node.

    Modify the prepend method in your DoublyLinkedList class located in src/playground/doubly.ts:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    prepend(data: T): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = newNode;
        <span class="hljs-built_in">this</span>.tail = newNode;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">let</span> prevHead = <span class="hljs-built_in">this</span>.head;
        newNode.next = prevHead;
        prevHead.prev = newNode;
        <span class="hljs-built_in">this</span>.head = newNode;
      }
    
      <span class="hljs-built_in">this</span>.len++;
    }
    

    The data prop holds the value for the new node. Here’s how prepend works:

    • It creates a new node with the given data.

    • If the list is empty (head is null), it sets both the head and tail to the new node.

    • If the list has nodes, it points the new node’s next to the current head.

    • It points the current head’s prev to the new node.

    • It sets the head to the new node.

    • It increases the list’s length by one.

    This runs in O(1) time because you only change a few pointers without looping.

    How to Append a Node in a Doubly Linked List

    The goal is to add a new node to the end of your doubly linked list and update the tail pointer to this new node.

    Modify the append method in your DoublyLinkedList:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    append(data: T): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = newNode;
        <span class="hljs-built_in">this</span>.tail = newNode;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">this</span>.tail!.next = newNode;
        newNode.prev = <span class="hljs-built_in">this</span>.tail;
        <span class="hljs-built_in">this</span>.tail = newNode;
      }
    
      <span class="hljs-built_in">this</span>.len++;
    }
    

    Here’s how append works:

    • The data prop holds the value for the new node.

    • It makes a new node with the given data.

    • It checks if the list is empty ( head is null ), and sets both the head and tail to the new node.

    • If the list has nodes, it points the current tail’s next to the new node.

    • It points the new node’s prev to the current tail.

    • It sets the tail to the new node.

    • It increases the list’s length by one.

    This runs in O(1) time because you only change a few pointers without looping.

    How to Delete the Head of a Doubly Linked List

    The goal is to remove the first node from your doubly linked list and return its data.

    Modify the deleteHead method in your DoublyLinkedList:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    deleteHead(): T | <span class="hljs-literal">null</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">let</span> removedItem = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">this</span>.head = removedItem.next;
        <span class="hljs-built_in">this</span>.head!.prev = <span class="hljs-literal">null</span>;
        removedItem.next = <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-built_in">this</span>.len--;
    
      <span class="hljs-keyword">return</span> removedItem.data;
    }
    

    Here’s how deleteHead works:

    • If the list is empty, it returns null because there’s no node to remove.

    • It creates a new variable called removedItem and stores the head node in it as the item to be removed.

    • If the list’s length is 1, it means the list has only one node, which acts as both the head and tail of the list. In this case, it sets both the head and tail to null.

    • If the list has multiple nodes, it moves the head to the next node.

    • It sets the new head‘s prev to null since it’s now the first node.

    • It clears the removed node’s next pointer.

    • It decreases the list’s length by one.

    • It returns the removed node’s data.

    This runs in O(1) time because you only change a few pointers without looping.

    How to Delete the Last Node of a Doubly Linked List

    The goal is to remove the last node from your doubly linked list and return its data.

    Modify the deleteTail method in your DoublyLinkedList:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    deleteTail(): T | <span class="hljs-literal">null</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.tail) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">let</span> removedItem = <span class="hljs-built_in">this</span>.tail;
    
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-built_in">this</span>.tail.prev;
        <span class="hljs-built_in">this</span>.tail!.next = <span class="hljs-literal">null</span>;
        removedItem.prev = <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-built_in">this</span>.len--;
    
      <span class="hljs-keyword">return</span> removedItem.data;
    }
    

    Here’s how deleteTail works:

    • It checks if the list is empty. If the tail is null, it returns null because there’s no node to remove.

    • It saves the tail node in a variable named removedItem as the node to be removed.

    • It checks if the list has one node. If the length is 1, it sets both head and tail to null.

    • If the list has multiple nodes, it moves the tail to the previous node.

    • It sets the new tail‘s next to null since it’s now the last node.

    • It clears the removed node’s prev pointer.

    • It decreases the list’s length by one.

    • It returns the removed node’s data.

    This runs in O(1) time because you only change a few pointers without looping.

    How to Delete a Node from a Doubly Linked List

    The goal is to remove the first node with the given value from your doubly linked list and return true if successful.

    Modify the delete method in your DoublyLinkedList:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">boolean</span> {
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">if</span> (!current) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
      <span class="hljs-keyword">if</span> (current.data === data) {
        <span class="hljs-built_in">this</span>.head = current.next;
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head) <span class="hljs-built_in">this</span>.head.prev = <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">else</span> <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.len--;
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-keyword">while</span> (current.next) {
        <span class="hljs-keyword">if</span> (current.next.data === data) {
          <span class="hljs-keyword">let</span> nodeToRemove = current.next;
          current.next = nodeToRemove.next;
          <span class="hljs-keyword">if</span> (current.next) current.next.prev = current;
          <span class="hljs-keyword">else</span> <span class="hljs-built_in">this</span>.tail = current;
          nodeToRemove.next = <span class="hljs-literal">null</span>;
          nodeToRemove.prev = <span class="hljs-literal">null</span>;
          <span class="hljs-built_in">this</span>.len--;
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
        current = current.next;
      }
    
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
    

    The data prop is the value to find and remove.

    Here’s how delete works:

    • It checks if the list is empty – if the head is null, returns false because there’s nothing to delete.

    • It checks if the head node’s value matches data and if it does, moves the head to the next node, sets the new head’s prev to null or clears the tail if empty, decreases the length, and returns true.

    • If the head doesn’t match, it creates a new pointer called current at the head.

    • It moves current through the list while a next node exists, checking if the next node’s value matches data.

    • If a match is found, it skips the next node by linking current’s next to the node after it.

    • It updates the next node’s prev to current or sets the tail to current if it’s the last node, clears the removed node’s pointers, decreases the length, and returns true.

    • If no match is found, it moves current to the next node and keeps checking.

    • If you reach the end without a match, it returns false.

    This runs in O(n) time because you may need to traverse the entire list to find the node.

    How to Find a Node in a Doubly Linked List

    The goal is to find the node at a specific position in your doubly linked list.

    Modify the find method in your DoublyLinkedList:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    find(idx: <span class="hljs-built_in">number</span>): N<T> | <span class="hljs-literal">null</span> {
      <span class="hljs-keyword">if</span> (idx < <span class="hljs-number">0</span> || idx >= <span class="hljs-built_in">this</span>.len) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">let</span> current: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">if</span> (idx <= <span class="hljs-built_in">this</span>.len / <span class="hljs-number">2</span>) {
        current = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < idx; i++) {
          current = current!.next;
        }
      } <span class="hljs-keyword">else</span> {
        current = <span class="hljs-built_in">this</span>.tail;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-built_in">this</span>.len - <span class="hljs-number">1</span>; i > idx; i--) {
          current = current?.prev ?? <span class="hljs-literal">null</span>;
        }
      }
    
      <span class="hljs-keyword">return</span> current;
    }
    

    The idx prop is the position in the list, starting at 0.

    Here’s how find works:

    • It checks if the index is valid. If idx is negative or too large, it returns null because no node exists.

    • It starts a new pointer called current at the head.

    • It checks if idx is in the first half of the list. If it is, it moves current forward idx times using the next pointer.

    • If idx is in the second half, it starts current at the tail and moves backward to the position using the prev pointer.

    • It returns the current node, which is at the given index, or null if the list is empty.

    This runs in O(n) time because you may move through half the list to find the node.

    How to Traverse a Doubly Linked List

    The goal is to return an array of all node data in your doubly linked list, either forward or backward. Modify the traverse method in your DoublyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    traverse(dir: <span class="hljs-string">"forward"</span> | <span class="hljs-string">"backward"</span> = <span class="hljs-string">"forward"</span>): T[] {
      <span class="hljs-keyword">const</span> isForward = dir === <span class="hljs-string">"forward"</span>;
      <span class="hljs-keyword">let</span> current = isForward ? <span class="hljs-built_in">this</span>.head : <span class="hljs-built_in">this</span>.tail;
      <span class="hljs-keyword">const</span> result: T[] = [];
    
      <span class="hljs-keyword">while</span> (current) {
        result.push(current.data);
        current = isForward ? current.next : current.prev;
      }
    
      <span class="hljs-keyword">return</span> result;
    }
    

    The dir prop sets whether to go forward (from head to tail) or backward (from tail to head).

    Here’s how traverse works:

    • It checks the direction and sets a flag to true if moving forward.

    • It starts a new pointer called current at the head if forward, or the tail if backward.

    • It creates an empty array to store the node data.

    • While current exists, it adds its data to the array.

    • It moves current to the next node if forward, or the previous node if backward.

    • It returns the array with all node data.

    This runs in O(n) time because you may need to visit every node in the list.

    How to Insert a Node at a Specific Position in a Doubly Linked List

    The goal is to add a new node at a specific index in your doubly linked list.

    Modify the insertAt method in your DoublyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    
    insertAt(idx: <span class="hljs-built_in">number</span>, data: T): <span class="hljs-built_in">boolean</span> {
      <span class="hljs-keyword">if</span> (idx < <span class="hljs-number">0</span> || idx > <span class="hljs-built_in">this</span>.len) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
      <span class="hljs-keyword">if</span> (idx === <span class="hljs-number">0</span>) {
        <span class="hljs-built_in">this</span>.prepend(data);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-keyword">if</span> (idx === <span class="hljs-built_in">this</span>.len) {
        <span class="hljs-built_in">this</span>.append(data);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.find(idx);
    
      <span class="hljs-keyword">if</span> (!current) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
      newNode.next = current;
      newNode.prev = current?.prev ?? <span class="hljs-literal">null</span>;
      current.prev!.next = newNode;
      current.prev = newNode;
    
      <span class="hljs-built_in">this</span>.len++;
    
      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
    

    The idx prop is the position in the list, and data is the value.

    Here’s how insertAt works:

    • It checks if the index is invalid, if idx is negative or greater than the list’s length, returns false.

    • If the index is 0, you are adding the new node at the beginning. Simply call prepend to add the node at the start and return true.

    • If the index is equal to the list’s length, you are adding the new node to the end of the list. In this case, you can call append to add the node at the end and return true.

    • If the previous conditions are not met, it creates a new node with the given data.

    • It finds the node at the given index using the find method and stores it as current.

    • If no node is found at the index, it returns false.

    • It points the new node’s next to current.

    • It points the new node’s prev to current’s previous node.

    • It points the previous node’s next to the new node.

    • It points current’s prev to the new node.

    • It increases the list’s length by one.

    • It returns true to show the node was successfully inserted.

    This runs in O(n) time because finding the index may require traversing the list.

    How to Test Your Doubly Linked List

    Awesome, what great progress! You’ve successfully implemented your doubly linked list. Here’s what the final implementation should look like:

    <span class="hljs-comment">// 📁 src/playground/doubly.ts</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> N<T> {
      data: T;
      next: N<T> | <span class="hljs-literal">null</span>;
      prev: N<T> | <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">data: T</span>) {
        <span class="hljs-built_in">this</span>.data = data;
        <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.prev = <span class="hljs-literal">null</span>;
      }
    }
    
    <span class="hljs-comment">/** Doubly linked list implementation */</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> DoublyLinkedList<T> {
      <span class="hljs-comment">/** Head node */</span>
      <span class="hljs-keyword">public</span> head: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** Tail node */</span>
      <span class="hljs-keyword">public</span> tail: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** List length */</span>
      <span class="hljs-keyword">public</span> len: <span class="hljs-built_in">number</span>;
    
      <span class="hljs-comment">/** Creates an empty list */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.len = <span class="hljs-number">0</span>;
      }
    
      <span class="hljs-comment">/** Adds node to list start */</span>
      prepend(data: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = newNode;
          <span class="hljs-built_in">this</span>.tail = newNode;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">let</span> prevHead = <span class="hljs-built_in">this</span>.head;
          newNode.next = prevHead;
          prevHead.prev = newNode;
          <span class="hljs-built_in">this</span>.head = newNode;
        }
    
        <span class="hljs-built_in">this</span>.len++;
      }
    
      <span class="hljs-comment">/** Adds node to list end */</span>
      append(data: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = newNode;
          <span class="hljs-built_in">this</span>.tail = newNode;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">this</span>.tail!.next = newNode;
          newNode.prev = <span class="hljs-built_in">this</span>.tail;
          <span class="hljs-built_in">this</span>.tail = newNode;
        }
    
        <span class="hljs-built_in">this</span>.len++;
      }
    
      <span class="hljs-comment">/** Removes and returns head node data */</span>
      deleteHead(): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
        <span class="hljs-keyword">let</span> removedItem = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
          <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">this</span>.head = removedItem.next;
          <span class="hljs-built_in">this</span>.head!.prev = <span class="hljs-literal">null</span>;
          removedItem.next = <span class="hljs-literal">null</span>;
        }
    
        <span class="hljs-built_in">this</span>.len--;
    
        <span class="hljs-keyword">return</span> removedItem.data;
      }
    
      <span class="hljs-comment">/** Removes and returns tail node data */</span>
      deleteTail(): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.tail) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
        <span class="hljs-keyword">let</span> removedItem = <span class="hljs-built_in">this</span>.tail;
    
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
          <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">this</span>.tail = <span class="hljs-built_in">this</span>.tail.prev;
          <span class="hljs-built_in">this</span>.tail!.next = <span class="hljs-literal">null</span>;
          removedItem.prev = <span class="hljs-literal">null</span>;
        }
    
        <span class="hljs-built_in">this</span>.len--;
    
        <span class="hljs-keyword">return</span> removedItem.data;
      }
    
      <span class="hljs-comment">/** Removes first node with given data */</span>
      <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">if</span> (!current) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
        <span class="hljs-keyword">if</span> (current.data === data) {
          <span class="hljs-built_in">this</span>.head = current.next;
          <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head) <span class="hljs-built_in">this</span>.head.prev = <span class="hljs-literal">null</span>;
          <span class="hljs-keyword">else</span> <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
          <span class="hljs-built_in">this</span>.len--;
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        <span class="hljs-keyword">while</span> (current.next) {
          <span class="hljs-keyword">if</span> (current.next.data === data) {
            <span class="hljs-keyword">let</span> nodeToRemove = current.next;
            current.next = nodeToRemove.next;
            <span class="hljs-keyword">if</span> (current.next) current.next.prev = current;
            <span class="hljs-keyword">else</span> <span class="hljs-built_in">this</span>.tail = current;
            nodeToRemove.next = <span class="hljs-literal">null</span>;
            nodeToRemove.prev = <span class="hljs-literal">null</span>;
            <span class="hljs-built_in">this</span>.len--;
            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
          }
          current = current.next;
        }
    
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    
      <span class="hljs-comment">/** Finds node at given index */</span>
      find(idx: <span class="hljs-built_in">number</span>): N<T> | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">if</span> (idx < <span class="hljs-number">0</span> || idx >= <span class="hljs-built_in">this</span>.len) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
        <span class="hljs-keyword">let</span> current: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">if</span> (idx <= <span class="hljs-built_in">this</span>.len / <span class="hljs-number">2</span>) {
          current = <span class="hljs-built_in">this</span>.head;
          <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < idx; i++) {
            current = current!.next;
          }
        } <span class="hljs-keyword">else</span> {
          current = <span class="hljs-built_in">this</span>.tail;
          <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-built_in">this</span>.len - <span class="hljs-number">1</span>; i > idx; i--) {
            current = current?.prev ?? <span class="hljs-literal">null</span>;
          }
        }
    
        <span class="hljs-keyword">return</span> current;
      }
    
      <span class="hljs-comment">/** Returns array of node data */</span>
      traverse(dir: <span class="hljs-string">"forward"</span> | <span class="hljs-string">"backward"</span> = <span class="hljs-string">"forward"</span>): T[] {
        <span class="hljs-keyword">const</span> isForward = dir === <span class="hljs-string">"forward"</span>;
        <span class="hljs-keyword">let</span> current = isForward ? <span class="hljs-built_in">this</span>.head : <span class="hljs-built_in">this</span>.tail;
        <span class="hljs-keyword">const</span> result: T[] = [];
    
        <span class="hljs-keyword">while</span> (current) {
          result.push(current.data);
          current = isForward ? current.next : current.prev;
        }
    
        <span class="hljs-keyword">return</span> result;
      }
    
      <span class="hljs-comment">/** Inserts node at given index */</span>
      insertAt(idx: <span class="hljs-built_in">number</span>, data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">if</span> (idx < <span class="hljs-number">0</span> || idx > <span class="hljs-built_in">this</span>.len) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
        <span class="hljs-keyword">if</span> (idx === <span class="hljs-number">0</span>) {
          <span class="hljs-built_in">this</span>.prepend(data);
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        <span class="hljs-keyword">if</span> (idx === <span class="hljs-built_in">this</span>.len) {
          <span class="hljs-built_in">this</span>.append(data);
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.find(idx);
    
        <span class="hljs-keyword">if</span> (!current) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
        newNode.next = current;
        newNode.prev = current?.prev ?? <span class="hljs-literal">null</span>;
        current.prev!.next = newNode;
        current.prev = newNode;
    
        <span class="hljs-built_in">this</span>.len++;
    
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    }
    

    Run the following command to make sure that your implementation is correct:

    npm run <span class="hljs-built_in">test</span>:file doubly
    

    If the tests run successfully, you’re good to go! If any tests fail, check src/examples/doubly.ts, fix the issue, and run the tests again.

    You’ve learned how to implement a linked node with two pointers. Doubly linked lists are useful in many scenarios, but like singly linked lists, they have a limitation you need to consider.

    Let’s say you have a music playlist, and every time you reach the end, you want to loop back to the first song instead of stopping.

    In both singly and doubly linked lists, once you reach the end, there’s no way to loop back to the first item because the last node points to null. So, what will you do if you want to create a music playlist using linked lists?

    That’s what you’ll learn in the next section of this tutorial!

    What is a Circular Linked List?

    So far, you’ve learned about singly and doubly linked lists, where the last item always points to null.

    Sometimes, you need to go back to the first item after reaching the last one. In this case, the tail‘s next pointer should point to the head instead of null.

    This is what a circular linked list is. In circular linked lists, the tail points to the head, letting you keep going through the list without stopping.

    In the next 2 sections, you’ll learn about two types of circular linked lists:

    • Circular Singly Linked List: Each node has one pointer to the next node, and the tail always points to the head.

    • Circular Doubly Linked List: Each node has two pointers to the next and previous nodes. The tail points to the head as its next node, and the head points to the tail as its previous node.

    Now, let’s dive deeper and learn how to create each of these lists.

    What is a Circular Singly Linked List?

    In a circular singly linked list, each node has only one pointer that points to the next node in the list. The main difference between a singly linked list and a circular singly linked list is the tail node.

    In a circular singly linked list, the tail always points to the head, forming a circle that allows continuous looping through the list.

    Diagram of a circular singly linked list with nodes labeled A, B, C, and D. Arrows indicate the "next" pointers, forming a loop. The head points to Node A, and the tail points to Node D.

    Now, let’s look at the node structure in a circular singly linked list.

    How to Create a Generic Node Structure for a Circular Singly Linked List

    The Node structure in a circular singly linked list has two parts: the data and a pointer to the next node.

    The data property holds the node’s value, and next points to the next node in the list.

    Open src/playground/circular-1.ts and modify the N class:

    <span class="hljs-comment">/** Node for circular singly linked list */</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> N<T> {
      <span class="hljs-comment">/** Node data */</span>
      <span class="hljs-keyword">public</span> data: T;
      <span class="hljs-comment">/** Next node reference */</span>
      <span class="hljs-keyword">public</span> next: N<T> | <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Creates a node with given data */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">data: T</span>) {
        <span class="hljs-built_in">this</span>.data = data;
        <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
      }
    }
    

    Here’s how the node structure works:

    • It builds a generic Node: Uses <T> to handle any data type, such as numbers or strings.

    • It stores the node’s value: Assigns the value to the data property.

    • It links to the next node: Sets the next pointer to the next node, null only during initialization. In a valid circular list, next always connects to a node.

    • It initializes the node: Takes a value in the constructor and assigns it to data.

    In a valid circular linked list, next never stays null.

    How to Implement a Circular Singly Linked List

    Once you have created your Node structure, you can start implementing the linked list.

    To get started, let’s open src/playground/circular-1.ts, where you’ll find the CircularSinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-1.ts</span>
    
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> N<T> {
      data: T;
      next: N<T> | <span class="hljs-literal">null</span>;
      prev: N<T> | <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">data: T</span>) {
        <span class="hljs-built_in">this</span>.data = data;
        <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.prev = <span class="hljs-literal">null</span>;
      }
    }
    
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CircularSinglyLinkedList<T> {
      <span class="hljs-comment">/** Head node */</span>
      <span class="hljs-keyword">public</span> head: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Adds node to list start */</span>
      prepend(data: T): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Adds node to list end */</span>
      append(data: T): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Removes head node */</span>
      deleteHead(): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Removes tail node */</span>
      deleteTail(): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    
      <span class="hljs-comment">/** Removes first node with given data */</span>
      <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    
      <span class="hljs-comment">/** Finds data at given index */</span>
      find(idx: <span class="hljs-built_in">number</span>): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Returns array of node data */</span>
      traverse(): T[] {
        <span class="hljs-keyword">return</span> [];
      }
    }
    

    You will complete each method by the end of this section.

    Like in earlier implementations, head will point to the first item in the list. If it’s null, the list is empty.

    Now, let’s go to the first method and learn how to add a node to the start of a circular linked list.

    How to Prepend a Node in a Circular Singly Linked List

    The goal is to add a new node to the beginning of your circular singly linked list and update the head pointer to this new node.

    Modify the prepend method in your CircularSinglyLinkedList class located in src/playground/circular-singly.ts:

    <span class="hljs-comment">// 📁 src/playground/circular-1.ts</span>
    
    prepend(data: T) {
      <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = newNode;
        newNode.next = newNode;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">let</span> last = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">while</span> (last.next !== <span class="hljs-built_in">this</span>.head) {
          <span class="hljs-keyword">if</span> (!last.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
          last = last.next;
        }
    
        last.next = newNode;
        newNode.next = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-built_in">this</span>.head = newNode;
      }
    }
    

    The data prop holds the value for the new node.

    Here’s how prepend works:

    • It creates a new node with the given value.

    • Checks if the list is empty. If the head is null, it sets the head to the new node and points its next to itself to form a single-node circle.

    • If the list has nodes, it finds the last node by moving through the list until it reaches the node whose next points to the head.

    • It points the last node’s next to the new node.

    • It points the new node’s next to the current head.

    • It sets the head to the new node.

    • Now, the new node is the head, and you’ve maintained the circular structure.

    This runs in O(n) time because you may need to traverse the entire list to find the last node.

    How to Append a Node in a Circular Singly Linked List

    The goal is to add a new node to the end of your circular singly linked list and maintain the circular structure.

    Let’s modify the append method in your CircularSinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-1.ts</span>
    
    append(data: T): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = newNode;
        newNode.next = <span class="hljs-built_in">this</span>.head;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-keyword">let</span> last = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">while</span> (last.next !== <span class="hljs-built_in">this</span>.head) {
          <span class="hljs-keyword">if</span> (!last.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
          last = last.next;
        }
    
        last.next = newNode;
        newNode.next = <span class="hljs-built_in">this</span>.head;
      }
    }
    

    The data prop holds the value for the new node.

    Here’s how append works:

    • It creates a new node with the given value.

    • It checks if the list is empty. If the head is null, it sets the head to the new node and points its next to itself to form a single-node circle.

    • If the list has nodes, it finds the last node by moving through the list until it reaches the node whose next points to the head.

    • It points the last node’s next to the new node.

    • It points the new node’s next to the head to keep the list circular.

    • Now, the new node is at the end, and you’ve maintained the circular structure.

    • It increases the list’s length by one.

    This runs in O(n) time because you may need to traverse the entire list to find the last node.

    How to Delete the Head of a Circular Singly Linked List

    The goal is to remove the first node from your circular singly linked list and update the head pointer.

    Modify the deleteHead method in your CircularSinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-1.ts</span>
    
    deleteHead(): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span>;
    
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.next === <span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">return</span>;
      }
    
      <span class="hljs-keyword">let</span> last = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">while</span> (last.next !== <span class="hljs-built_in">this</span>.head) {
        <span class="hljs-keyword">if</span> (!last.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
        last = last.next;
      }
    
      <span class="hljs-keyword">let</span> newHead = <span class="hljs-built_in">this</span>.head.next;
      last.next = newHead;
      <span class="hljs-built_in">this</span>.head = newHead;
    }
    

    Here’s how deleteHead works:

    • It checks if the list is empty and stops the operation if the head is null because there’s no node to remove.

    • It checks if the list has one node: if the head‘s next points to itself, it sets the head to null to empty the list.

    • If the list has multiple nodes, it finds the last node by moving through the list until it reaches the node whose next points to the head.

    • It sets the current head’s next node as the new head.

    • It points the tail node’s next to the new head to keep the list circular.

    • It sets the head to the new head.

    This runs in O(n) time because you may need to traverse the entire list to find the last node.

    How to Delete the Last Node of a Circular Singly Linked List

    The goal is to remove the last node from your circular singly linked list and maintain the circular structure.

    Modify the deleteTail method in your CircularSinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-1.ts</span>
    
    deleteTail(): <span class="hljs-built_in">boolean</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.next === <span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-keyword">let</span> current: N<T> = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-keyword">let</span> prev: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">while</span> (current.next !== <span class="hljs-built_in">this</span>.head) {
        prev = current;
        current = current.next!;
      }
    
      prev!.next = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
    

    Here’s how deleteTail works:

    • It checks if the list is empty. If the head is null, it returns false because there’s no node to remove.

    • It checks if the list has one node. If the head’s next points to itself, it sets the head to null and returns true.

    • If the list has multiple nodes, it starts a new pointer called current at the head and a prev pointer at null.

    • It moves current through the list until its next points to the head, keeping prev one node behind.

    • It points prev’s next to the head to skip the last node and keep the list circular.

    • It returns true to show the tail was removed.

    This runs in O(n) time because you may need to traverse the entire list to find the last node.

    How to Delete a Node from a Circular Singly Linked List

    The goal is to remove the first node with the given value from your circular singly linked list and return true if successful.

    Modify the delete method in your CircularSinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-1.ts</span>
    
    <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">boolean</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.data === data) {
        <span class="hljs-built_in">this</span>.deleteHead();
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-keyword">let</span> current: N<T> = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-keyword">let</span> prev: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">do</span> {
        <span class="hljs-keyword">if</span> (current.data === data) {
          prev!.next = current.next;
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        prev = current;
        current = current.next!;
      } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
    

    The data prop is the value to find and remove.

    You must use a do-while loop instead of a simple while loop in the method because it ensures you always process the head node’s data at least once before checking if you’ve returned to the head.

    In a circular singly linked list, you start at the head and keep moving to the next node until you come back to the head.

    A simple while loop might skip the head if checked first, but a do-while loop makes sure the head‘s data is included.

    Here’s how delete works:

    • It checks if the list is empty. If the head is null, it returns false because there’s nothing to delete.

    • It checks if the head node’s value matches data. If it does, it calls deleteHead to remove the head and returns true.

    • If the head doesn’t match, it starts a new pointer called current at the head and a prev pointer at null.

    • It moves current through the list, keeping prev one node behind, until it circles back to the head.

    • If current’s value matches data, it links prev’s next to current’s next to skip the matched node.

    • It returns true if a match is removed, or false if it finds no match after a full loop.

    This runs in O(n) time because you may need to traverse the entire list to find the node.

    How to Find a Node in a Circular Singly Linked List

    The goal is to find the data at a specific index in your circular singly linked list.

    Modify the find method in your CircularSinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-1.ts</span>
    
    find(idx: <span class="hljs-built_in">number</span>): T | <span class="hljs-literal">null</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head || idx < <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    
      <span class="hljs-keyword">do</span> {
        <span class="hljs-keyword">if</span> (!current.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
    
        <span class="hljs-keyword">if</span> (count === idx) {
          <span class="hljs-keyword">return</span> current.data;
        }
    
        count++;
        current = current.next;
      } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
      <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    }
    

    The idx prop is the position in the list.

    Here’s how find works:

    • It checks if the list is empty or the index is negative. If so, it returns null because no data exists.

    • It creates a new pointer called current at the head and set a counter to 0.

    • It moves current through the list, checking each node until it circles back to the head.

    • If the counter equals idx, it returns the current node’s data.

    • If not, it increases the counter and moves current to the next node.

    • If you circle back to the head without finding the index, it returns null.

    This runs in O(n) time because you may need to traverse the entire list to find the index.

    How to Traverse a Circular Singly Linked List

    The goal is to return an array of all node data in your circular singly linked list.

    Modify the traverse method in your CircularSinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-1.ts</span>
    
    traverse(): T[] {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> [];
      <span class="hljs-keyword">const</span> result: T[] = [];
    
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">do</span> {
        result.push(current.data);
        current = current.next!;
      } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
      <span class="hljs-keyword">return</span> result;
    }
    

    Here’s how traverse works:

    • It checks if the list is empty. If the head is null, it returns an empty array.

    • It creates an empty array to store the node data.

    • It creates a new pointer called current at the head.

    • It moves current through the list, adding each node’s data to the array.

    • It continues moving current to the next node until you circle back to the head.

    • It returns the array with all node data.

    This runs in O(n) time because you need to visit every node in the list.

    How to Insert a Node at a Specific Position in a Circular Singly Linked List

    The goal is to add a new node at a specific index in your circular singly linked list.

    Modify the insertAt method in your CircularSinglyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-1.ts</span>
    
    insertAt(data: T, idx: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">boolean</span> {
      <span class="hljs-keyword">if</span> (idx < <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
      <span class="hljs-keyword">if</span> (idx === <span class="hljs-number">0</span>) {
        <span class="hljs-built_in">this</span>.prepend(data);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-keyword">if</span> (idx === <span class="hljs-number">0</span>) {
          <span class="hljs-built_in">this</span>.prepend(data);
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    
      <span class="hljs-keyword">let</span> current: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-keyword">let</span> prev: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
      <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    
      <span class="hljs-keyword">do</span> {
        <span class="hljs-keyword">if</span> (count === idx) {
          <span class="hljs-keyword">const</span> newN = <span class="hljs-keyword">new</span> N(data);
          newN.next = current;
          prev!.next = newN;
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
        prev = current;
        current = current!.next;
        count++;
      } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
      <span class="hljs-keyword">if</span> (count === idx) {
        <span class="hljs-built_in">this</span>.append(data);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
    

    The data prop is the value, and idx is the position in the list.

    Here’s how insertAt works:

    • If the index is negative, it returns false because it’s invalid.

    • If the index is 0, it calls prepend to add the node at the start and returns true.

    • It creates a new pointer called current at the head, a prev pointer at null, and a counter at 0.

    • It moves current through the list, keeping prev one node behind, until you circle back to the head.

    • If the counter equals idx, it creates a new node, point its next to current, points prev’s next to the new node, and returns true.

    • If you circle back to the head and the counter equals idx, it calls append to add the node at the end and returns true.

    • Finally, if the index is not found, it returns false.

    This runs in O(n) time because you may need to traverse the entire list to find the index.

    How to Test Your Circular Singly Linked List

    Perfect! You are done with the circular singly linked list and now you are ready to test your implementation.

    Your final implementation should look something like this:

    <span class="hljs-comment">/** Node for circular singly linked list */</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> N<T> {
      <span class="hljs-comment">/** Node data */</span>
      <span class="hljs-keyword">public</span> data: T;
      <span class="hljs-comment">/** Next node reference */</span>
      <span class="hljs-keyword">public</span> next: N<T> | <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Creates a node with given data */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">data: T</span>) {
        <span class="hljs-built_in">this</span>.data = data;
        <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
      }
    }
    
    <span class="hljs-comment">/** Circular singly linked list implementation */</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CircularSinglyLinkedList<T> {
      <span class="hljs-comment">/** Head node */</span>
      <span class="hljs-keyword">public</span> head: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Adds node to list start */</span>
      prepend(data: T) {
        <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = newNode;
          newNode.next = newNode;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">let</span> last = <span class="hljs-built_in">this</span>.head;
    
          <span class="hljs-keyword">while</span> (last.next !== <span class="hljs-built_in">this</span>.head) {
            <span class="hljs-keyword">if</span> (!last.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
            last = last.next;
          }
    
          last.next = newNode;
          newNode.next = <span class="hljs-built_in">this</span>.head;
          <span class="hljs-built_in">this</span>.head = newNode;
        }
      }
    
      <span class="hljs-comment">/** Adds node to list end */</span>
      append(data: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = newNode;
          newNode.next = <span class="hljs-built_in">this</span>.head;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-keyword">let</span> last = <span class="hljs-built_in">this</span>.head;
    
          <span class="hljs-keyword">while</span> (last.next !== <span class="hljs-built_in">this</span>.head) {
            <span class="hljs-keyword">if</span> (!last.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
            last = last.next;
          }
    
          last.next = newNode;
          newNode.next = <span class="hljs-built_in">this</span>.head;
        }
      }
    
      <span class="hljs-comment">/** Removes head node */</span>
      deleteHead(): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span>;
    
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.next === <span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
          <span class="hljs-keyword">return</span>;
        }
    
        <span class="hljs-keyword">let</span> last = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">while</span> (last.next !== <span class="hljs-built_in">this</span>.head) {
          <span class="hljs-keyword">if</span> (!last.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
          last = last.next;
        }
    
        <span class="hljs-keyword">let</span> newHead = <span class="hljs-built_in">this</span>.head.next;
        last.next = newHead;
        <span class="hljs-built_in">this</span>.head = newHead;
      }
    
      <span class="hljs-comment">/** Removes tail node */</span>
      deleteTail(): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.next === <span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        <span class="hljs-keyword">let</span> current: N<T> = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">let</span> prev: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
        <span class="hljs-keyword">while</span> (current.next !== <span class="hljs-built_in">this</span>.head) {
          prev = current;
          current = current.next!;
        }
    
        prev!.next = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-comment">/** Removes first node with given data */</span>
      <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.head.data === data) {
          <span class="hljs-built_in">this</span>.deleteHead();
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        <span class="hljs-keyword">let</span> current: N<T> = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">let</span> prev: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
    
        <span class="hljs-keyword">do</span> {
          <span class="hljs-keyword">if</span> (current.data === data) {
            prev!.next = current.next;
            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
          }
    
          prev = current;
          current = current.next!;
        } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    
      <span class="hljs-comment">/** Finds data at given index */</span>
      find(idx: <span class="hljs-built_in">number</span>): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head || idx < <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    
        <span class="hljs-keyword">do</span> {
          <span class="hljs-keyword">if</span> (!current.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
    
          <span class="hljs-keyword">if</span> (count === idx) {
            <span class="hljs-keyword">return</span> current.data;
          }
    
          count++;
          current = current.next;
        } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Returns array of node data */</span>
      traverse(): T[] {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> [];
        <span class="hljs-keyword">const</span> result: T[] = [];
    
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">do</span> {
          result.push(current.data);
          current = current.next!;
        } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
        <span class="hljs-keyword">return</span> result;
      }
    
      <span class="hljs-comment">/** Inserts node at given index */</span>
      insertAt(data: T, idx: <span class="hljs-built_in">number</span>): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">if</span> (idx < <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
        <span class="hljs-keyword">if</span> (idx === <span class="hljs-number">0</span>) {
          <span class="hljs-built_in">this</span>.prepend(data);
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
          <span class="hljs-keyword">if</span> (idx === <span class="hljs-number">0</span>) {
            <span class="hljs-built_in">this</span>.prepend(data);
            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
          }
          <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
        }
    
        <span class="hljs-keyword">let</span> current: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">let</span> prev: N<T> | <span class="hljs-literal">null</span> = <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">let</span> count = <span class="hljs-number">0</span>;
    
        <span class="hljs-keyword">do</span> {
          <span class="hljs-keyword">if</span> (count === idx) {
            <span class="hljs-keyword">const</span> newN = <span class="hljs-keyword">new</span> N(data);
            newN.next = current;
            prev!.next = newN;
            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
          }
          prev = current;
          current = current!.next;
          count++;
        } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
        <span class="hljs-keyword">if</span> (count === idx) {
          <span class="hljs-built_in">this</span>.append(data);
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    }
    

    Now, let’s run the following command to test the linked list:

    npm run <span class="hljs-built_in">test</span>:file circular-1
    

    If the tests run successfully, you’re all set! If any tests fail, check src/examples/circular-1.ts, fix the issue, and run the tests again.

    That’s it, you’ve completed your first circular linked list implementation.

    In the last section, you’ll learn how to create a circular linked list with two pointers instead of one.

    What is a Circular Doubly Linked List?

    A circular doubly linked list forms a loop where nodes connect to both the next and previous nodes.

    Diagram of a Circular Doubly Linked List with nodes labeled A, B, C, and D, showing next and prev pointers. Node A is connected to Head, and Node D is connected to Tail. Links form a circular structure.

    The head links back to the tail, and the tail to the head, so you can have endless navigation in either direction.

    How to Create a Generic Node Structure for a Circular Doubly Linked List

    The Node structure in a circular doubly linked list has three parts: the data, a pointer to the next node, and a pointer to the previous node.

    The data property holds the node’s value, next points to the next node, and prev points to the previous node in the list.

    Open src/playground/circular-2.ts and modify the N class:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    <span class="hljs-comment">/** Node for circular doubly linked list */</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> N<T> {
      <span class="hljs-comment">/** Node data */</span>
      <span class="hljs-keyword">public</span> data: T;
      <span class="hljs-comment">/** Next node reference */</span>
      <span class="hljs-keyword">public</span> next: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** Previous node reference */</span>
      <span class="hljs-keyword">public</span> prev: N<T> | <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Creates a node with given data */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">data: T</span>) {
        <span class="hljs-built_in">this</span>.data = data;
        <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.prev = <span class="hljs-literal">null</span>;
      }
    }
    

    Here’s how the node structure works:

    • It builds a generic Node: Uses <T> to handle any data type.

    • It stores the node’s value: Assigns the value to the data property.

    • It links to the next node: Sets the next pointer to the next node, null only during initialization. In a valid circular list, next always connects to a node.

    • It links to the previous node: Sets the prev pointer to the previous node, null only during initialization. In a valid circular list, prev always connects to a node.

    • It initializes the node: Takes a value in the constructor and assigns it to data.

    In a valid circular doubly linked list, next and prev never stay null.

    How to Implement a Circular Doubly Linked List

    You’ve created the Node structure for your circular doubly linked list. Now, you can start building the linked list itself. To get started, open src/playground/circular-2.ts, where you’ll find the CircularDoublyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> N<T> {
      <span class="hljs-comment">/** Node data */</span>
      <span class="hljs-keyword">public</span> data: T;
      <span class="hljs-comment">/** Next node reference */</span>
      <span class="hljs-keyword">public</span> next: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** Previous node reference */</span>
      <span class="hljs-keyword">public</span> prev: N<T> | <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Creates a node with given data */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">data: T</span>) {
        <span class="hljs-built_in">this</span>.data = data;
        <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.prev = <span class="hljs-literal">null</span>;
      }
    }
    
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CircularDoublyLinkedList<T> {
      <span class="hljs-comment">/** Head node */</span>
      <span class="hljs-keyword">public</span> head: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** Tail node */</span>
      <span class="hljs-keyword">public</span> tail: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** List length */</span>
      <span class="hljs-keyword">public</span> len: <span class="hljs-built_in">number</span>;
    
      <span class="hljs-comment">/** Creates an empty list */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.len = <span class="hljs-number">0</span>;
      }
    
      <span class="hljs-comment">/** Adds node to list end */</span>
      append(data: T): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Removes and returns tail node data */</span>
      deleteTail(): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Adds node to list start */</span>
      prepend(data: T): <span class="hljs-built_in">void</span> {}
    
      <span class="hljs-comment">/** Removes and returns head node data */</span>
      deleteHead(): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Finds node at given index */</span>
      find(idx: <span class="hljs-built_in">number</span>): N<T> | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-comment">/** Removes first node with given data */</span>
      <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    
      <span class="hljs-comment">/** Returns array of node data */</span>
      traverse(): T[] {
        <span class="hljs-keyword">return</span> [];
      }
    
      <span class="hljs-comment">/** Inserts node at given index */</span>
      insertAt(idx: <span class="hljs-built_in">number</span>, data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    }
    

    The head points to the first node and links backward to the tail to form a circular loop. It is null when the list is empty.

    The tail points to the last node and links forward to the head to complete the circle. It is also null when empty.

    The length, len, tracks the number of nodes. It starts at 0 and changes as you add or remove nodes.

    Now, let’s move to the first method and learn how to add a node to the start of a circular doubly linked list.

    How to Prepend a Node in a Circular Doubly Linked List

    The goal is to add a new node to the beginning of your circular doubly linked list and update the head pointer to this new node.

    Modify the prepend method in your CircularDoublyLinkedList class located in src/playground/circular-2.ts:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    prepend(data: T): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = newNode;
        <span class="hljs-built_in">this</span>.tail = newNode;
        newNode.next = newNode;
        newNode.prev = newNode;
      } <span class="hljs-keyword">else</span> {
        newNode.next = <span class="hljs-built_in">this</span>.head;
        newNode.prev = <span class="hljs-built_in">this</span>.tail;
        <span class="hljs-built_in">this</span>.head!.prev = newNode;
        <span class="hljs-built_in">this</span>.tail!.next = newNode;
        <span class="hljs-built_in">this</span>.head = newNode;
      }
    
      <span class="hljs-built_in">this</span>.len++;
    }
    

    The data prop holds the value for the new node.

    Here’s how prepend works:

    • It creates a new node with the given data.

    • it checks if the list is empty. If the head is null, it sets both head and tail to the new node and links its next and prev to itself to form a single-node circle.

    • If the list has nodes, it points the new node’s next to the current head and its prev to the current tail.

    • It points the current head’s prev to the new node.

    • It points the current tail’s next to the new node to keep the list circular.

    • It sets the head to the new node.

    • It increases the list’s length by one.

    This runs in O(1) time because you only change a few pointers without looping.

    How to Append a Node in a Circular Doubly Linked List

    The goal is to add a new node to the end of your circular doubly linked list and update the tail pointer to this new node.

    Modify the append method in your CircularDoublyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    append(data: T): <span class="hljs-built_in">void</span> {
      <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
        <span class="hljs-built_in">this</span>.head = newNode;
        <span class="hljs-built_in">this</span>.tail = newNode;
        newNode.next = newNode;
        newNode.prev = newNode;
      } <span class="hljs-keyword">else</span> {
        newNode.next = <span class="hljs-built_in">this</span>.head;
        newNode.prev = <span class="hljs-built_in">this</span>.tail;
        <span class="hljs-built_in">this</span>.tail!.next = newNode;
        <span class="hljs-built_in">this</span>.head!.prev = newNode;
        <span class="hljs-built_in">this</span>.tail = newNode;
      }
    
      <span class="hljs-built_in">this</span>.len++;
    }
    

    The data prop holds the value for the new node, like a number or string.

    Here’s how append works:

    • It makes a new node with the given value.

    • If the list is empty, then it sets both head and tail to the new node, and links its next and prev to itself to form a single-node circle.

    • If the list has nodes, it points the new node’s next to the head to maintain the circular loop.

    • It points the new node’s prev to the current tail.

    • It points the current tail’s next to the new node.

    • It points the head’s prev to the new node to complete the circle.

    • It sets the tail to the new node.

    • It increases the list’s length by one.

    This runs in O(1) time because you only change a few pointers without looping.

    How to Delete the Last Node of a Circular Doubly Linked List

    The goal is to remove the last node from your circular doubly linked list and return its data.

    Modify the deleteTail method in your CircularDoublyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    deleteTail(): T | <span class="hljs-literal">null</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.tail) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">let</span> removedItem = <span class="hljs-built_in">this</span>.tail;
    
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-built_in">this</span>.tail.prev;
        <span class="hljs-built_in">this</span>.tail!.next = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-built_in">this</span>.head!.prev = <span class="hljs-built_in">this</span>.tail;
      }
    
      removedItem.next = <span class="hljs-literal">null</span>;
      removedItem.prev = <span class="hljs-literal">null</span>;
      <span class="hljs-built_in">this</span>.len--;
    
      <span class="hljs-keyword">return</span> removedItem.data;
    }
    

    Here’s how deleteTail works:

    • If the list is empty, then it returns null because there’s no node to remove.

    • It declares a variable called removedItem and stores the tail node in it to keep track of the node you want to remove.

    • It checks if the list has one node. If the length is 1, it sets both head and tail to null.

    • If the list has multiple nodes, it moves the tail to the previous node.

    • It points the new tail’s next to the head to keep the circular loop.

    • It points the head’s prev to the new tail to maintain the circle.

    • It clears the removed node’s next and prev pointers.

    • It decreases the list’s length by one.

    • It returns the removed node’s data.

    This runs in O(1) time because you only change a few pointers without looping.

    How to Delete the Head of a Circular Doubly Linked List

    The goal is to remove the first node from your circular doubly linked list and return its data.

    Modify the deleteHead method in your CircularDoublyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    deleteHead(): T | <span class="hljs-literal">null</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
      <span class="hljs-keyword">let</span> removedItem = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
      } <span class="hljs-keyword">else</span> {
        <span class="hljs-built_in">this</span>.head = removedItem.next;
        <span class="hljs-built_in">this</span>.head!.prev = <span class="hljs-built_in">this</span>.tail;
        <span class="hljs-built_in">this</span>.tail!.next = <span class="hljs-built_in">this</span>.head;
      }
    
      <span class="hljs-built_in">this</span>.len--;
      removedItem.next = <span class="hljs-literal">null</span>;
      removedItem.prev = <span class="hljs-literal">null</span>;
      <span class="hljs-keyword">return</span> removedItem.data;
    }
    

    Here’s how deleteHead works:

    • If the list is empty then it returns null because there’s no node to remove.

    • It declares a variable called removedItem and stores the head node in it to keep track of the node you want to remove.

    • It checks if the list has one node. If the length is 1, it sets both head and tail to null.

    • If the list has multiple nodes, it moves the head to the next node to make it the new first node.

    • It points the new head’s prev to the tail to maintain the backward loop in the circular structure.

    • It points the tail’s next to the new head to keep the forward loop in the circular structure.

    • It clears the removed node’s next and prev pointers to disconnect it from the list.

    • It decreases the list’s length by one.

    • It returns the removed node’s data.

    This runs in O(1) time because you only change a few pointers without looping.

    How to Find a Node in a Circular Doubly Linked List

    The goal is to find the node at a specific index in your circular doubly linked list.

    Modify the find method in your CircularDoublyLinkedList class:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    find(idx: <span class="hljs-built_in">number</span>): N<T> | <span class="hljs-literal">null</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head || idx < <span class="hljs-number">0</span> || idx >= <span class="hljs-built_in">this</span>.len) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
      }
    
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < idx; i++) {
        current = current!.next!;
      }
    
      <span class="hljs-keyword">return</span> current;
    }
    

    The idx prop is the position in the list. Here’s how find works:

    • It checks if the list is empty or the index is invalid. If the head is null, idx is negative, or idx is too large, it returns null because no node exists.

    • It creates a new pointer called current at the head.

    • It moves current forward through the next pointers as many times as the index value.

    • Once you exit the loop, it returns the current node, which is at the specified index.

    This runs in O(n) time because you may need to traverse up to n nodes to reach the index.

    How to Traverse a Circular Doubly Linked List

    The goal is to return an array of all node data in your circular doubly linked list.

    Modify the traverse method in your CircularDoublyLinkedList:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    traverse(): T[] {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> [];
    
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
      <span class="hljs-keyword">const</span> result: T[] = [];
    
      <span class="hljs-keyword">do</span> {
        <span class="hljs-keyword">if</span> (!current.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
    
        result.push(current.data);
    
        current = current.next;
      } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
      <span class="hljs-keyword">return</span> result;
    }
    

    Here’s how traverse works:

    • If the list is empty, it returns an empty array.

    • It creates an empty array to store the node data.

    • It creates a new pointer called current at the head.

    • It adds the current node’s data to the array.

    • It moves current to the next node using the next pointer.

    • It repeats adding data and moving current until you circle back to the head.

    • It returns the array with all node data.

    This runs in O(n) time because you need to visit every node in the list.

    How to Delete a Node from a Circular Doubly Linked List

    The goal is to remove the first node with the given value from your circular doubly linked list and return true if successful.

    Modify the delete method in your CircularDoublyLinkedList class located in:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">boolean</span> {
      <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
      <span class="hljs-keyword">do</span> {
        <span class="hljs-keyword">if</span> (current.data === data) {
          <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
            <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
            <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
          } <span class="hljs-keyword">else</span> {
            current.prev!.next = current.next;
            current.next!.prev = current.prev;
            <span class="hljs-keyword">if</span> (current === <span class="hljs-built_in">this</span>.head) {
              <span class="hljs-built_in">this</span>.head = current.next;
            }
            <span class="hljs-keyword">if</span> (current === <span class="hljs-built_in">this</span>.tail) {
              <span class="hljs-built_in">this</span>.tail = current.prev;
            }
          }
          <span class="hljs-built_in">this</span>.len--;
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
        current = current.next!;
      } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
      <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    }
    

    The data prop is the value to find and remove. Here’s how delete works:

    • If the list is empty, then it returns false because there’s nothing to delete.

    • It creates a new pointer called current at the head.

    • It moves current through the list and checks each node’s value until you circle back to the head.

    • If current’s value matches data, it checks if the list has one node, if so, it sets both head and tail to null since the single node within the list is both the head and the tail.

    • If the list has multiple nodes, it links the previous node’s next to the next node and the next node’s prev to the previous node to skip current.

    • If current is the head, it updates the head to the next node. If current is the tail, it updates the tail to the previous node.

    • It decreases the list’s length by one and returns true.

    • If no match, it moves current to the next node and continues checking.

    • If you circle back to the head without a match, it returns false.

    This runs in O(n) time because you may need to traverse the entire list to find the node.

    How to Insert a Node at a Specific Position in a Circular Doubly Linked List

    The goal is to add a new node at a specific index in your circular doubly linked list.

    Modify the insertAt method in your CircularDoublyLinkedList:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    insertAt(idx: <span class="hljs-built_in">number</span>, data: T): <span class="hljs-built_in">boolean</span> {
      <span class="hljs-keyword">if</span> (idx < <span class="hljs-number">0</span> || idx > <span class="hljs-built_in">this</span>.len) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
      <span class="hljs-keyword">if</span> (idx === <span class="hljs-number">0</span>) {
        <span class="hljs-built_in">this</span>.prepend(data);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-keyword">if</span> (idx === <span class="hljs-built_in">this</span>.len) {
        <span class="hljs-built_in">this</span>.append(data);
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    
      <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
      <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.find(idx);
    
      <span class="hljs-keyword">if</span> (!current) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
      newNode.next = current;
      newNode.prev = current!.prev;
      current.prev!.next = newNode;
      current.prev = newNode;
    
      <span class="hljs-built_in">this</span>.len++;
      <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
    }
    

    The idx prop is the position in the list, and data is the value.

    Here’s how insertAt works:

    • If idx is negative or greater than the list’s length, then the idx prop is an invalid index, and you should return false to stop the operation.

    • If the index is 0, it calls prepend to add the node at the start and returns true.

    • If the idx equals the list’s length, you are adding a new tail. In this case, it calls append to add the node at the end and returns true.

    • If the above conditions are not met, it creates a new node with the given data.

    • It finds the node at the given index using the find method and stores it as current.

    • If no node is found at the idx, it returns false.

    • It points the new node’s next to current. This sets the new node to precede current in the forward direction of the circular list.

    • This sets the new node’s prev to current’s prev node. This links the new node to the node before current and keeps the backward link in the circular list intact.

    • It sets the previous node’s next to the new node, so the node before current now links to the new node. This keeps the circular loop intact by making sure the forward chain skips the original predecessor of current and includes the new node.

    • It sets current‘s prev to the new node. This completes the insertion by making current link back to the new node and keeping the circular structure with correct two-way links.

    • It increases the list’s length by one.

    • It returns true to show the node was inserted.

    This runs in O(n) time because finding the index may require traversing the list.

    How to Test Your Circular Doubly Linked List

    Great job! You’ve completed the circular doubly linked list, and now you’re ready to test your implementation.

    Your final implementation should look like this:

    <span class="hljs-comment">// 📁 src/playground/circular-2.ts</span>
    
    <span class="hljs-comment">/** Node for circular doubly linked list */</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> N<T> {
      <span class="hljs-comment">/** Node data */</span>
      <span class="hljs-keyword">public</span> data;
      <span class="hljs-comment">/** Next node reference */</span>
      <span class="hljs-keyword">public</span> next: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** Previous node reference */</span>
      <span class="hljs-keyword">public</span> prev: N<T> | <span class="hljs-literal">null</span>;
    
      <span class="hljs-comment">/** Creates a node with given data */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params">data: T</span>) {
        <span class="hljs-built_in">this</span>.data = data;
        <span class="hljs-built_in">this</span>.next = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.prev = <span class="hljs-literal">null</span>;
      }
    }
    
    <span class="hljs-comment">/** Circular doubly linked list implementation */</span>
    <span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> CircularDoublyLinkedList<T> {
      <span class="hljs-comment">/** Head node */</span>
      <span class="hljs-keyword">public</span> head: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** Tail node */</span>
      <span class="hljs-keyword">public</span> tail: N<T> | <span class="hljs-literal">null</span>;
      <span class="hljs-comment">/** List length */</span>
      <span class="hljs-keyword">public</span> len: <span class="hljs-built_in">number</span>;
    
      <span class="hljs-comment">/** Creates an empty list */</span>
      <span class="hljs-keyword">constructor</span>(<span class="hljs-params"></span>) {
        <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.len = <span class="hljs-number">0</span>;
      }
    
      <span class="hljs-comment">/** Adds node to list end */</span>
      append(data: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = newNode;
          <span class="hljs-built_in">this</span>.tail = newNode;
          newNode.next = newNode;
          newNode.prev = newNode;
        } <span class="hljs-keyword">else</span> {
          newNode.next = <span class="hljs-built_in">this</span>.head;
          newNode.prev = <span class="hljs-built_in">this</span>.tail;
          <span class="hljs-built_in">this</span>.tail!.next = newNode;
          <span class="hljs-built_in">this</span>.head!.prev = newNode;
          <span class="hljs-built_in">this</span>.tail = newNode;
        }
    
        <span class="hljs-built_in">this</span>.len++;
      }
    
      <span class="hljs-comment">/** Removes and returns tail node data */</span>
      deleteTail(): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.tail) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
        <span class="hljs-keyword">let</span> removedItem = <span class="hljs-built_in">this</span>.tail;
    
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
          <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">this</span>.tail = <span class="hljs-built_in">this</span>.tail.prev;
          <span class="hljs-built_in">this</span>.tail!.next = <span class="hljs-built_in">this</span>.head;
          <span class="hljs-built_in">this</span>.head!.prev = <span class="hljs-built_in">this</span>.tail;
        }
    
        removedItem.next = <span class="hljs-literal">null</span>;
        removedItem.prev = <span class="hljs-literal">null</span>;
        <span class="hljs-built_in">this</span>.len--;
    
        <span class="hljs-keyword">return</span> removedItem.data;
      }
    
      <span class="hljs-comment">/** Adds node to list start */</span>
      prepend(data: T): <span class="hljs-built_in">void</span> {
        <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
    
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) {
          <span class="hljs-built_in">this</span>.head = newNode;
          <span class="hljs-built_in">this</span>.tail = newNode;
          newNode.next = newNode;
          newNode.prev = newNode;
        } <span class="hljs-keyword">else</span> {
          newNode.next = <span class="hljs-built_in">this</span>.head;
          newNode.prev = <span class="hljs-built_in">this</span>.tail;
          <span class="hljs-built_in">this</span>.head!.prev = newNode;
          <span class="hljs-built_in">this</span>.tail!.next = newNode;
          <span class="hljs-built_in">this</span>.head = newNode;
        }
    
        <span class="hljs-built_in">this</span>.len++;
      }
    
      <span class="hljs-comment">/** Removes and returns head node data */</span>
      deleteHead(): T | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
    
        <span class="hljs-keyword">let</span> removedItem = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
          <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
          <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
        } <span class="hljs-keyword">else</span> {
          <span class="hljs-built_in">this</span>.head = removedItem.next;
          <span class="hljs-built_in">this</span>.head!.prev = <span class="hljs-built_in">this</span>.tail;
          <span class="hljs-built_in">this</span>.tail!.next = <span class="hljs-built_in">this</span>.head;
        }
    
        <span class="hljs-built_in">this</span>.len--;
        removedItem.next = <span class="hljs-literal">null</span>;
        removedItem.prev = <span class="hljs-literal">null</span>;
        <span class="hljs-keyword">return</span> removedItem.data;
      }
    
      <span class="hljs-comment">/** Finds node at given index */</span>
      find(idx: <span class="hljs-built_in">number</span>): N<T> | <span class="hljs-literal">null</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head || idx < <span class="hljs-number">0</span> || idx >= <span class="hljs-built_in">this</span>.len) {
          <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;
        }
    
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < idx; i++) {
          current = current!.next!;
        }
    
        <span class="hljs-keyword">return</span> current;
      }
    
      <span class="hljs-comment">/** Removes first node with given data */</span>
      <span class="hljs-keyword">delete</span>(data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
    
        <span class="hljs-keyword">do</span> {
          <span class="hljs-keyword">if</span> (current.data === data) {
            <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>.len === <span class="hljs-number">1</span>) {
              <span class="hljs-built_in">this</span>.head = <span class="hljs-literal">null</span>;
              <span class="hljs-built_in">this</span>.tail = <span class="hljs-literal">null</span>;
            } <span class="hljs-keyword">else</span> {
              current.prev!.next = current.next;
              current.next!.prev = current.prev;
              <span class="hljs-keyword">if</span> (current === <span class="hljs-built_in">this</span>.head) {
                <span class="hljs-built_in">this</span>.head = current.next;
              }
              <span class="hljs-keyword">if</span> (current === <span class="hljs-built_in">this</span>.tail) {
                <span class="hljs-built_in">this</span>.tail = current.prev;
              }
            }
            <span class="hljs-built_in">this</span>.len--;
            <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
          }
          current = current.next!;
        } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
        <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
      }
    
      <span class="hljs-comment">/** Returns array of node data */</span>
      traverse(): T[] {
        <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>.head) <span class="hljs-keyword">return</span> [];
    
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.head;
        <span class="hljs-keyword">const</span> result: T[] = [];
    
        <span class="hljs-keyword">do</span> {
          <span class="hljs-keyword">if</span> (!current.next) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"invalid list"</span>);
    
          result.push(current.data);
    
          current = current.next;
        } <span class="hljs-keyword">while</span> (current !== <span class="hljs-built_in">this</span>.head);
    
        <span class="hljs-keyword">return</span> result;
      }
    
      <span class="hljs-comment">/** Inserts node at given index */</span>
      insertAt(idx: <span class="hljs-built_in">number</span>, data: T): <span class="hljs-built_in">boolean</span> {
        <span class="hljs-keyword">if</span> (idx < <span class="hljs-number">0</span> || idx > <span class="hljs-built_in">this</span>.len) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
        <span class="hljs-keyword">if</span> (idx === <span class="hljs-number">0</span>) {
          <span class="hljs-built_in">this</span>.prepend(data);
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        <span class="hljs-keyword">if</span> (idx === <span class="hljs-built_in">this</span>.len) {
          <span class="hljs-built_in">this</span>.append(data);
          <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
        }
    
        <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> N(data);
        <span class="hljs-keyword">let</span> current = <span class="hljs-built_in">this</span>.find(idx);
    
        <span class="hljs-keyword">if</span> (!current) <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;
    
        newNode.next = current;
        newNode.prev = current!.prev;
        current.prev!.next = newNode;
        current.prev = newNode;
    
        <span class="hljs-built_in">this</span>.len++;
        <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;
      }
    }
    

    Run the following command to test the linked list:

    npm run <span class="hljs-built_in">test</span>:file circular-2
    

    If the tests pass successfully, you’re all set! If any tests fail, review src/examples/circular-2.ts, fix the issues, and run the tests again.

    When to Use Linked Lists (and When to Avoid Them)

    Linked lists are powerful data structures, but they’re not always the best choice. So it’s important to know when to use them and when to choose an alternative.

    Why Use Linked Lists?

    Linked lists are great for situations that need dynamic data or flexible navigation.

    Their main benefits include:

    • Dynamic size: Add or remove nodes without resizing, unlike arrays that need reallocation.

    • Efficient insertions/deletions: Operations like prepend or delete are quick (O(1) at known positions), which is very ideal for frequent updates.

    • Flexible traversal: Doubly and circular lists allow you to move forward or backward, which makes them helpful for complex navigation patterns.

    Real-World Use Cases

    You should consider using linked lists in scenarios where the data is frequently updated or requires cyclic or bidirectional access:

    • Browser history: A doubly linked list keeps track of visited pages and lets users easily move back and forth. Adding a new page (prepend) or removing one (delete) is fast, and the list grows dynamically as users browse.

    • Music playlists: Circular doubly linked lists are used for looping playlists in apps like Spotify. Users can easily skip forward (next) or backward (prev), and new songs (append) fit smoothly into the loop.

    • Undo/redo functionality: Text editors use doubly linked lists to store actions. Each edit is a node, and moving backward (undo) or forward (redo) navigates through the list.

    • Task scheduling: Circular singly linked lists are used for round-robin scheduling in operating systems. Each process is a node, and the list cycles through them to allocate CPU time. New tasks are added using append.

    When Not to Use Linked Lists

    Despite their strengths, linked lists have weaknesses in some situations because of their structure:

    • Slow random access: Reaching an index requires you to traverse from the head (O(n)), unlike arrays, which have O(1) access.

    • High memory overhead: Each node in a linked list stores pointers (next, prev), which uses more memory than arrays. This can be an issue for large datasets.

    • Poor search performance: Finding a value requires checking each node (O(n)), which is slower than hash tables (O(1)) or binary search trees (O(log n)).

    Better Alternatives for Specific Cases

    In some cases, other data structures outperform linked lists:

    • Random access: Use arrays or dynamic arrays (like JavaScript’s Array) for quick indexing. For instance, if you need to show a table in a web app, an array’s O(1) access lets you quickly reach any row.

    • Frequent searches: Hash tables (like JavaScript’s Map) are better for fast lookups. For example, a contact list app that searches by name would use a hash table to speed up the process.

    • Memory-constrained environments: Arrays use less memory for large, fixed-size datasets, such as image processing buffers in graphics apps.

    The key takeaway is that linked lists are great when your data needs dynamic growth, frequent insertions or deletions, or cyclic navigation, like in playlists or history features.

    Avoid using linked lists for random access, frequent searches, or memory-sensitive tasks, where arrays, hash tables, or trees are better options.

    You can experiment with your src/playground implementations to see how linked lists fit your project’s needs.

    Conclusion

    Congratulations on finishing this handbook! 🥳 You’ve learned how to implement different types of linked lists using TypeScript, including singly linked lists, doubly linked lists, and circular linked lists.

    By understanding these linked lists, you’re well-prepared to work with more complex data structures.

    Thanks for following along with this tutorial. You can follow me on X, where I share more useful tips on data structures and web development.

    Happy coding!

    Source: freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More 

    Facebook Twitter Reddit Email Copy Link
    Previous ArticleWhy Public Wi-Fi Is Dangerous – And How to Protect Yourself
    Next Article A Beginner’s Guide to Graphs — From Google Maps to Chessboards

    Related Posts

    Development

    How to Make Bluetooth on Android More Reliable

    September 4, 2025
    Development

    Learn Mandarin Chinese for Beginners – Full HSK 1 Level

    September 4, 2025
    Leave A Reply Cancel Reply

    For security, use of Google's reCAPTCHA service is required which is subject to the Google Privacy Policy and Terms of Use.

    Continue Reading

    CVE-2025-6065 – WordPress Image Resizer On The Fly Remote Code Execution Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-54060 – WeGIA SQL Injection Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    CVE-2025-7123 – Campcodes Complaint Management System SQL Injection Vulnerability

    Common Vulnerabilities and Exposures (CVEs)

    WinRAR zero-day exploited in espionage attacks against high-value targets

    Development

    Highlights

    News & Updates

    The Alters shattered my expectations of reality and questioned my life’s decisions

    May 22, 2025

    What can and can’t be is a matter choices in life, and The Alters goes…

    Ongoing Attacks Exploit GeoServer RCE Flaw (CVE-2024-36401) to Install NetCat and XMRig CoinMiner

    July 10, 2025

    Stack years of Microsoft 365 — this deal would have been a bargain in 2013

    July 11, 2025

    Buy the iPhone 16 or wait for iPhone 17? Here’s how I help friends and family decide

    August 14, 2025
    © DevStackTips 2025. All rights reserved.
    • Contact
    • Privacy Policy

    Type above and press Enter to search. Press Esc to cancel.