”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 掌握如何在 JavaScript 中实现双向链表

掌握如何在 JavaScript 中实现双向链表

发布于2024-11-20
浏览:129

Hi ?, welcome back. It's been exactly 6 days since we started this journey together. I want to believe it has been an awesome experience. Yesterday, we learned about Singly Linked Lists, how to implement them in JavaScript with a few basic operations. Today, we'll learn how to implement Doubly Linked Lists in JavaScript. So, let's dive in! ?‍♂️

Course Outline

  1. Introduction
  2. Implementing Doubly Linked Lists
    • Creating the Node class
    • Insert at the beginning
    • Insert at the end
    • Insert at a specific position
    • Delete a node
    • Traverse forward
    • Traverse backward
    • Search for a node
  3. Usage Example
  4. Conclusion


Introduction

When it comes to data structures, the Doubly Linked List is a versatile and powerful tool. ?️ It's a type of linked list where each node contains a data field and two pointers, one pointing to the next node and the other pointing to the previous node. This bidirectional traversal capability makes doubly linked lists particularly useful in various programming scenarios like:

  • Implementing queues or stacks with efficient insertion and deletion.
  • Implementing undo/redo functionality in applications. ↩️↪️
  • Implementing browser's forward and back button. ??
  • Implementing music players' next and previous song feature. ?⏮️⏭️
  • Implementing LRU (Least Recently Used) cache.

Implementing Doubly Linked Lists

A doubly-linked list is like a singly-linked list, but with an extra feature: you can move in both directions. This means you can go from the start to the end of the list. In the next section, we'll learn how to implement a doubly-linked list in JavaScript. ?

By the way, if you missed the previous article on singly-linked lists, you can check it out here.

As we already know from last article, we'll need to create a Node class to represent each element in the list. This is similar to what we did in the previous article except that we'll need to add a prev property to the Node class to keep track of the previous node (which is the exact feature that makes a doubly-linked list different from a singly-linked list).

Creating the Node class


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


This is a simple visualization of what we've just created.

Master How Doubly Linked List is implemented in JavaScript

Now that we have our Node class, let's implement the DoublyLinkedList class.


class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // Methods will be implemented here ?
}


  • Explanation: The constructor initializes the list with head and tail set to null, indicating that the list is empty. The length property is initialized to 0, representing the number of nodes in the list.

Now that we have our DoublyLinkedList class, let's start implementing our methods.

Insert at the beginning


class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  insertAtBeginning(data) {
    const newNode = new Node(data);
    if (!this.head) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      newNode.next = this.head;
      this.head.prev = newNode;
      this.head = newNode;
    }
    this.length  ;
  }

  // Other methods will be implemented here ?
}


  • Explanation: This method inserts a new node at the beginning of the list.
    • A new node is created with the provided data.
    • If the list is empty (!this.head), both head and tail are set to the new node.
    • If the list is not empty, the new node's next pointer is set to the current head, and the current head's prev pointer is updated to point to the new node. Finally, the head is updated to the new node.
    • The length of the list is incremented (since we add to the list).

Insert at the end


class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // Previous methods  here ?
  // .
  // .
  // .

  // Insert at the end
  insertAtEnd(data) {
    const newNode = new Node(data);
    if (!this.tail) {
      this.head = newNode;
      this.tail = newNode;
    } else {
      newNode.prev = this.tail;
      this.tail.next = newNode;
      this.tail = newNode;
    }
    this.length  ;
  }

  // Other methods will be implemented here ?
}


  • Explanation: This method inserts a new node at the end of the list.
    • A new node is created with the provided data.
    • If the list is empty (!this.tail), both head and tail are set to the new node.
    • If the list is not empty, the new node's prev pointer is set to the current tail, and the current tail's next pointer is updated to point to the new node. Finally, the tail is updated to the new node.
    • The length of the list is incremented.

Insert at a specific position


class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // Previous methods here ?
  // .
  // .
  // .

  // Insert at a specific position
  insertAtPosition(data, position) {
    if (position  this.length) {
      return false; // Invalid position
    }
    if (position === 0) {
      this.insertAtBeginning(data);
      return true;
    }
    if (position === this.length) {
      this.insertAtEnd(data);
      return true;
    }
    const newNode = new Node(data);
    let current = this.head;
    for (let i = 0; i 

  • Explanation: This method inserts a new node at a specified position in the list.
    • It first checks if the position is valid (between 0 and this.length).
    • If the position is 0, it calls insertAtBeginning. If the position is equal to this.length, it calls insertAtEnd.
    • For other positions, a new node is created, and the method traverses the list to find the node just before the specified position.
    • The new node's pointers are set accordingly, and the length is incremented.

Delete a node


class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // Previous methods here ?
  // .
  // .
  // .

  // Delete a node
  deleteNode(data) {
    if (!this.head) return false;
    let current = this.head;
    while (current) {
      if (current.data === data) {
        if (current === this.head && current === this.tail) {
          this.head = null;
          this.tail = null;
        } else if (current === this.head) {
          this.head = current.next;
          this.head.prev = null;
        } else if (current === this.tail) {
          this.tail = current.prev;
          this.tail.next = null;
        } else {
          current.prev.next = current.next;
          current.next.prev = current.prev;
        }
        this.length--;
        return true;
      }
      current = current.next;
    }
    return false; // Node not found
  }

  // Other methods will be implemented here ?
}


  • Explanation: This method deletes a node with the specified data from the list.
    • If the list is empty, it returns false.
    • It traverses the list to find the node with the matching data.
    • Depending on the position of the node (head, tail, or middle), it updates the pointers accordingly.
    • The length is decremented, and the method returns true if the node was found and deleted.

Traverse forward


class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // Previous methods here ?
  // .
  // .
  // .

  // Traverse forward
  traverseForward() {
    let current = this.head;
    while (current) {
      console.log(current.data);
      current = current.next;
    }
  }

  // Other methods will be implemented here ?
}


  • Explanation: This method traverses the list from the head to the tail, printing the data of each node.
    • It starts at the head and continues until it reaches the end of the list (current becomes null).

Traverse backward


class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // Previous methods here ?
  // .
  // .
  // .

  // Traverse backward
  traverseBackward() {
    let current = this.tail;
    while (current) {
      console.log(current.data);
      current = current.prev;
    }
  }
}


  • Explanation: This method traverses the list from the tail to the head, printing the data of each node.
    • It starts at the tail and continues until it reaches the beginning of the list (current becomes null).

Search for a node


class DoublyLinkedList {
  constructor() {
    this.head = null;
    this.tail = null;
    this.length = 0;
  }

  // Previous methods here ?
  // .
  // .
  // .

  // Search for a node
  search(data) {
    let current = this.head;
    let index = 0;
    while (current) {
      if (current.data === data) {
        return index;
      }
      current = current.next;
      index  ;
    }
    return -1; // Node not found
  }

  // Other methods will be implemented here ?
}


  • Explanation: This method searches for a node with the specified data and returns its index.
    • It traverses the list from the head, checking each node's data.
    • If a match is found, it returns the index; otherwise, it returns -1 if the node is not found.

Usage Example

Here's a simple example of how to use our Doubly Linked List:


const list = new DoublyLinkedList();

list.insertAtEnd(1);
list.insertAtEnd(2);
list.insertAtEnd(3);
list.insertAtBeginning(0);
list.insertAtPosition(1.5, 2);

console.log("Forward traversal:");
list.traverseForward();

console.log("Backward traversal:");
list.traverseBackward();

console.log("Search for 2:", list.search(2));

list.deleteNode(1.5);

console.log("After deletion:");
list.traverseForward();




Conclusion

In this article, we've learned about the basics operations of doubly linked lists and how to implement
them in JavaScript. In the next article, we'll be learning about Circular Linked Lists, then we can solve some leetcode problems to deepen our understanding.



Stay Updated and Connected

To ensure you don't miss any part of this series and to connect with me for more in-depth
discussions on Software Development (Web, Server, Mobile or Scraping / Automation), data
structures and algorithms, and other exciting tech topics, follow me on:

  • GitHub
  • Linkedin
  • X (Twitter)

Stay tuned and happy coding ?‍??

版本声明 本文转载于:https://dev.to/emmanuelayinde/master-how-doubly-linked-list-is-implemented-in-javascript-ogi?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何在 CSS 中使用 :after 和 :hover 动态设置列表项的样式?
    如何在 CSS 中使用 :after 和 :hover 动态设置列表项的样式?
    结合 :after 和 :hover 进行动态列表样式在 CSS 中,将 :after 等伪元素与悬停状态结合起来可以增强列表项的视觉效果。假设您有一个列表,其中所选项目使用 :after 显示箭头形状。要为悬停的项目实现类似的效果,请按照下列步骤操作:提供的 CSS 代码定义了一个样式列表,其中所...
    编程 发布于2024-11-20
  • 如何在不阻塞 UI 线程的情况下在 WPF 操作中引入延迟?
    如何在不阻塞 UI 线程的情况下在 WPF 操作中引入延迟?
    使用替代方法在 WPF 操作中实现延迟当尝试在 WPF 中执行操作之前引入延迟时,避免使用 Thread.Sleep 很重要,因为这方法会阻塞 UI 线程并可能导致用户界面无响应。相反,请考虑利用异步编程技术。DispatcherTimer 方法一个选择是使用 DispatcherTimer。此计时...
    编程 发布于2024-11-20
  • 如何在 PHP 中使用 cURL 将 POST 数据发送到 URL?
    如何在 PHP 中使用 cURL 将 POST 数据发送到 URL?
    在 PHP 中将 POST 数据发送到 URL当您需要在不依赖 HTML 表单的情况下将 POST 数据发送到 URL 时,PHP 的cURL 扩展提供了一个强大的解决方案。实现方法如下:使用 cURL:使用curl_init( $url ) 初始化 cURL 会话。将 $url 替换为目标 URL...
    编程 发布于2024-11-20
  • 在 Go 中使用 WebSocket 进行实时通信
    在 Go 中使用 WebSocket 进行实时通信
    构建需要实时更新的应用程序(例如聊天应用程序、实时通知或协作工具)需要一种比传统 HTTP 更快、更具交互性的通信方法。这就是 WebSockets 发挥作用的地方!今天,我们将探讨如何在 Go 中使用 WebSocket,以便您可以向应用程序添加实时功能。 在这篇文章中,我们将介绍: WebSoc...
    编程 发布于2024-11-20
  • 大批
    大批
    方法是可以在对象上调用的 fns 数组是对象,因此它们在 JS 中也有方法。 slice(begin):将数组的一部分提取到新数组中,而不改变原始数组。 let arr = ['a','b','c','d','e']; // Usecase: Extract till index p...
    编程 发布于2024-11-20
  • Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta:列偏移的删除和恢复Bootstrap 4 在其 Beta 1 版本中引入了重大更改柱子偏移了。然而,随着 Beta 2 的后续发布,这些变化已经逆转。从 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    编程 发布于2024-11-20
  • 如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    在 PHP 中组合关联数组在 PHP 中,将两个关联数组组合成一个数组是一项常见任务。考虑以下请求:问题描述:提供的代码定义了两个关联数组,$array1 和 $array2。目标是创建一个新数组 $array3,它合并两个数组中的所有键值对。 此外,提供的数组具有唯一的 ID,而名称可能重合。要求...
    编程 发布于2024-11-20
  • 除了“if”语句之外:还有什么地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    除了“if”语句之外:还有什么地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    无需强制转换即可上下文转换为 bool您的类定义了对 bool 的显式转换,使您能够在条件语句中直接使用其实例“t”。然而,这种显式转换提出了一个问题:“t”在哪里可以在不进行强制转换的情况下用作 bool?上下文转换场景C 标准指定了四种值可以根据上下文转换为的主要场景bool:语句:if、whi...
    编程 发布于2024-11-20
  • 如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    MySQL配置不正确:相对路径的问题在Django中运行python manage.py runserver时,可能会遇到以下错误:ImproperlyConfigured: Error loading MySQLdb module: dlopen(/Library/Python/2.7/site-...
    编程 发布于2024-11-20
  • 如何在 MySQL 中确定一周的第一天?
    如何在 MySQL 中确定一周的第一天?
    在 MySQL 中确定一周的第一天使用日期范围时,通常需要确定一周的第一天对于给定的日期。在 MySQL 中,根据所需的一周开始日期有不同的方法。从星期日开始一周获取从 开始的一周的第一天星期日,使用以下公式:DATE_ADD(mydate, INTERVAL(1-DAYOFWEEK(mydate)...
    编程 发布于2024-11-20
  • 哪个调用约定负责堆栈清理?
    哪个调用约定负责堆栈清理?
    调用约定:stdcall 与 cdecl在编程中,调用约定定义参数如何在函数之间传递。两个常见的调用约定是 stdcall 和 cdecl.1。 cdecl函数调用当调用cdecl函数时,调用者不负责清理堆栈。编译器根据函数的调用约定生成处理堆栈清理的代码。2。混合调用约定通常不建议混合调用约定。然...
    编程 发布于2024-11-20
  • 尽管代码有效,为什么 POST 请求无法捕获 PHP 中的输入?
    尽管代码有效,为什么 POST 请求无法捕获 PHP 中的输入?
    解决 PHP 中的 POST 请求故障在提供的代码片段中:action=''而不是:action="<?php echo $_SERVER['PHP_SELF'];?>";?>"检查 $_POST数组:表单提交后使用 var_dump 检查 $_POST 数...
    编程 发布于2024-11-20
  • 为什么 Go 函数不能直接返回多个值?
    为什么 Go 函数不能直接返回多个值?
    Go 返回多个值问题当尝试在 Go 中返回多个值时,为什么某些语法有效而其他语法无效似乎令人困惑。为了说明这一点,请考虑以下两个代码片段:func FindUserInfo(id string) (Info, bool) { it, present := all[id] return...
    编程 发布于2024-11-20
  • 为什么在使用 `go` 语句时要在主 Goroutine 中计算 `input.Text()` ?
    为什么在使用 `go` 语句时要在主 Goroutine 中计算 `input.Text()` ?
    为什么在主 Goroutine 中计算 input.Text()在Go 编程语言的第 8 章中,以下语句是关于并发 echo 服务器:由 go 启动的函数的参数在执行 go 语句本身时进行评估;因此 input.Text() 在主 goroutine 中被求值。这条语句的意思是,当执行 go 语句时...
    编程 发布于2024-11-20
  • 为什么我的Go HTTP客户端访问Github时提示“您对该站点的访问已被限制”?
    为什么我的Go HTTP客户端访问Github时提示“您对该站点的访问已被限制”?
    解决 Go HTTP 客户端中“您对该站点的访问已被限制”的问题使用 Go 的 HTTP 客户端访问 Github 资源可能会触发“您对该网站的访问已被限制”错误。在 AWS 上运行的 EC2 实例中从 Github 检索 tar.gz 等文件时,尤其会遇到此问题。潜在原因问题可能源于过时的软件配置...
    编程 发布于2024-11-20

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3