JavaScript 实现链表数据结构详解

作者:函数茶馆时间:2026-11-01 01:48:07浏览:191

本文详细解析如何使用 JavaScript 实现链表数据结构。从 Node 节点类的定义出发,逐步演示 LinkNodeList 类的构建,涵盖 push、insert、getVal、remove 及 toString 等核心方法的逻辑与代码实现,帮助开发者深入理解链表的内存分配与指针操作机制。

链表的基本概念

根据《算法第四版》的定义,链表是一种递归数据结构:它要么为空(null),要么是一个指向节点(node)的引用,该节点包含一个泛型元素以及指向另一条链表的引用。如果这听起来有些抽象,我们可以用更通俗的方式解释。

我们熟悉数组,在存储多个元素时,数组是最常见的选择。但数组的大小是固定的,若要在数组中随意插入或删除元素,往往需要移动其他元素,这种操作的开销较高。

链表由有序元素组成,与数组不同,链表中的元素在内存中并非连续存放。每个元素由一个存储数据本身的节点和一个指向下一个元素的引用(即指针或链接)构成。因此,链表在添加或删除元素时,无需移动其他元素,具有更高的灵活性。

在查找方面,数组可以通过索引直接访问任意位置的元素,而链表必须从起点开始遍历,直到找到目标元素。下面通过一张示意图来直观展示链表的结构:

链表的基本概念

使用 JavaScript 实现链表

接下来,我们将使用 JavaScript 代码实现一个链表。在刷 LeetCode 链表相关题目时,你可能会经常看到类似这样的代码片段:

链表的基本概念

这部分代码用于创建链表中的每个节点,这里我们使用 ES6 的类(class)语法来实现。

Node 类定义

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

链表类的构建

class LinkNodeList {
  constructor() {
    this.head = null;
    this.count = 0;
  }
}

head 属性表示链表的头节点,count 属性记录链表中节点的数量。我们需要实现哪些具体方法呢?

  • push(element):向链表尾部添加一个新元素。
  • insert(element, position):在链表的指定位置插入一个新元素。
  • getVal(index):返回指定位置的元素值。如果该位置不存在,则返回 undefined。
  • remove(position):从链表的指定位置移除一个元素。
  • isEmpty():如果链表为空,返回 true;如果链表长度大于 0,返回 false。
  • size():返回链表中包含的元素个数,类似于数组的 length 属性。
  • toString():返回表示整个链表的字符串形式。

size 方法实现

class LinkNodeList {
  constructor() {
    this.head = null;
    this.count = 0;
  }
  ···
  size() {
        return this.count;
  }
  ···
}

利用 count 属性记录节点数量,直接返回 count 即可获取链表大小。

isEmpty 方法实现

class LinkNodeList {
  constructor() {
    this.head = null;
    this.count = 0;
  }
  ···
  isEmpty() {
        return this.size() === 0;
  }
  ···
}

只需判断 size() 的返回值是否为 0 即可确定链表是否为空。

push 方法实现

向链表尾部添加一个新元素。

class LinkNodeList {
  constructor() {
    this.head = null;
    this.count = 0;
  }
  ···
  push(element) {
    const node = new Node(element);
    
    if (this.head === null) {
        this.head = node;
    } else {
        let current; = this.head;
        while (current.next) {
            current = current.next;
        }
        current.next = node;
    }
    this.count++;
  }
  ···
}

要在链表尾部添加元素,首先需通过 Node 类创建新节点。如果当前 head 不存在,说明链表为空,直接将 head 指向新节点即可。否则,遍历找到链表的最后一个节点,将其 next 指向新节点,并将 count 加 1。

链表的基本概念

getVal 方法实现

获取指定位置的元素值。

class LinkNodeList {
  constructor() {
    this.head = null;
    this.count = 0;
  }
  ···
  getVal(index) {
    if (index > 0 && index <= this.count) {
        let node = this.head;
        for (let i = 0; i < index; i++) {
                node = node.next;
        }
        return node;
    }
    return undefined;
   }
  ···
}

接收要查找的位置索引,首先判断该位置是否有效。如果无效,直接返回 undefined。

若位置有效,则从头节点开始循环遍历,直到找到目标索引对应的节点。循环结束时,node 即为该位置元素的引用。

insert 方法实现

在链表的指定位置插入一个新元素。

class LinkNodeList {
  constructor() {
    this.head = null;
    this.count = 0;
  }
  ···
  insert(ele, index) {
    if (index >= 0 && index <= this.count) {
        const node = new Node(ele);
        if (index === 0) {
            const current = this.head;
            node.next = current;
            this.head = node;
        } else {
            const prev = this.getVal(index - 1);
            node.next = prev.next;
            prev.next = node;
        }
        this.count++;
        return true;
    }
    return false;
   }
  ···
}

首先判断插入位置是否有效,若无效则直接返回 false,表示插入失败。

若位置有效,则创建新节点。如果插入位置为 0(即头节点),需将新节点的 next 指向当前头节点,并将 head 更新为新节点。

链表的基本概念

若插入位置在其他地方,需获取插入位置的前一个节点 prev,将新节点的 next 指向 prev 的下一个节点,然后将 prev 的 next 重新指向新节点。

链表的基本概念

只要插入操作有效,都需要对 count 进行更新。

remove 方法实现

从链表中移除一个元素。

class LinkNodeList {
  constructor() {
    this.head = null;
    this.count = 0;
  }
  ···
  remove(index) {
    if (index >= 0 && index < this.count) {
            let current = this.head;
            // 移除第一项
            if (index === 0) {
                    this.head = current.next;
            } else {
                    const prev = this.getVal(index - 1);
                    current = prev.next;
                    prev.next = current.next;
            }
            this.count--;
            return current.val;
    }

    return undefined;
  }
  ···
}

首先判断要移除的位置是否有效,若无效则返回 undefined。

如果移除的是第一个节点,直接将 head 指向下一个节点即可。

链表的基本概念

如果移除的是其他位置的节点,需获取该位置的前一个节点 prev,将 prev 的 next 指向当前节点的下一个节点(即 current.next)。

链表的基本概念

只要移除操作有效,都需要对 count 进行更新。

toString 方法实现

返回表示整个链表的字符串。由于列表项使用了 Node 类,需要重写继承自 JavaScript 对象默认的 toString 方法,使其仅输出元素的值。

class LinkNodeList {
  constructor() {
    this.head = null;
    this.count = 0;
  }
  ···
    toString() {
        if (this.head === null) {
            return "";
        }
        let objString = `${this.head.val}`;
        let current = this.head.next;
        for (let i = 1; i < this.size() && current !== null; i++) {
            objString = `${objString}=>${current.val}`;
            current = current.next;
        }
        return objString;
    }
  ···
}

如果 head 不存在,说明是空链表,直接返回空字符串。

接下来,将链表的每个元素通过“=>”符号拼接成字符串。下面查看实际运行效果。

链表的基本概念

以上即为使用 JavaScript 实现链表数据结构的详细代码解析,更多关于 JS 链表实现的资料,请关注本站其他相关文章!

作者最新文章