【JS 中的数据结构-链表】

本文探讨了JavaScript中数组与链表的优缺点,包括数组的固定长度限制与链表的动态管理,以及单向链表、双向链表和循环链表的结构与操作。通过实例展示了链表如何解决数组的问题,并对比了它们在性能和灵活性上的差异。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

数组VS 链表

  • 在很多编程语言中,数组的初始化长度都是固定的,如果数组已被数据填满,再要加入新的元素是非常困难的。而且,对于数组的删除和添加操作,通常需要将数组中的其他元素向前或者向后平移,这些操作也是十分繁琐的。其特点:
  • 数组是一种线性数据结构
  • 数组中存储的是连续的内存空间和相同类型的数据
  • 链表是一组节点组成的集合,每个节点都使用一个对象的引用来指向它的后一个节点。指向另一节点的引用讲做链。下面我画了一个简单的链接结构图
    在这里插入图片描述
  • 使用链表结构可以克服数组链表需要预先知道数据大小的缺点,链表结构可以充分利用计算机内存空间,实现灵活的内存动态管理。
  • 链表失去了数组随机读取的优点,同时链表由于增加了结点的指针域,空间开销比较大。
  • 链表的起始点的确定比较麻烦,因此很多链表的实现都会在链表的最前面添加一个特殊的节点,称为 头节点,表示链表的头部。
    链表又包括 单向链表,双向链表,循环链表

单向链表

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

class LinkedList {
  constructor() {
    this.head = new Node('head');
  }
  display() {
    let current = this.head;
    const res = [];
    while (!(current.next === null)) {
      res.push(current.next.data);
      current = current.next;
    }

    return res;
  }

  find(val) {
    let current = this.head;
    while (current.next !== null && current.data !== val) {
      current = current.next;
    }
    return current ? current : -1;
  }

  insert(newVal, target) {
    //  在目标节点后面插入值
    const newNode = new Node(newVal);
    const currentNode = this.find(target);
    newNode.next = currentNode.next;
    currentNode.next = newNode;
  }

  findPre(val) {
    let current = this.head;
    while (current.next !== null && current.next.data !== val) {
      current = current.next;
    }
    return current ? current : -1;
  }

  remove(val) {
    if (val === 'head') return false;
    const prevNode = this.findPre(val);
    if (prevNode === -1) return false;
    if (!(prevNode.next === null)) {
      prevNode.next = prevNode.next.next;
      return true;
    }
  }
  push(val) {
    let current = this.head;
    let newNode = new Node(val);
    while (current.next !== null) {
      current = current.next;
    }
    current.next = newNode;
  }
  unshift(val) {
    let newNode = new Node(val);
    this.insert(newNode, 'head');
  }
}


双向链表

双链表以类似的方式工作,但还有一个引用字段,称为“prev”字段。有了这个额外的字段,您就能够知道当前结点的前一个结点。
在这里插入图片描述

class doubleLinkNode {
  constructor(data) {
    this.data = data;
    this.next = null;
    this.pre=null;
  }
}

class DoubleLink {
  constructor() {
    this.head = new DoubleLinkNode('head');
  }
  find(val) {
    let current = this.head;
    while (current.next !== null && current.data !== val) {
      current = current.next;
    }
    return current ? current : -1;
  }
  insert(newVal, target) {
    //  在目标节点后面插入值
    const newNode = new DoubleLinkNode(newVal);
    const currentNode = this.find(target);
    newNode.next = currentNode.next;
    newNode.pre = currentNode;
    currentNode.next = newNode;
  }

  remove(val) {
    if (val === 'head') return false;
    let currentNode = this.find(val);
    if (currentNode === -1) return false;
    currentNode.previous.next = currentNode.next;
    currentNode.next = null;
    currentNode.previous = null;
    if (currentNode.next !== null) {
      currentNode.next.pre = currentNode.pre;
    }
    return true;
  }
  display() {
    let current = this.head;
    const res = [];
    while (!(current.next === null)) {
      res.push(current.next.data);
      current = current.next;
    }
  }

  push(val) {
    let current = this.head;
    let newNode = new DoubleLinkNode(val);
    while (current.next !== null) {
      current = current.next;
    }
    current.next = newNode;
    newNode.pre = current;
  }

  unshift(val) {
    let newNode = new DoubleLinkNode(val);
    this.insert(newNode, 'head');
  }
}

循环链表

循环链表和单链表相似,节点类型都是一样,唯一的区别是,在创建循环链表的时候,让其头节点的 next 属性执行它本身,代码和单向链表类似

head.next = head;

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值