js使用链表实现音乐播放器(新增,下一首播放,置顶,删除)

什么是链表

链表是一种线性数据结构,与数组类似,它用于存储一系列元素。不过,与数组在内存中连续存储元素不同,链表中的元素(称为节点)在内存中可以是非连续存放的。每个节点包含两部分:一部分存储数据,另一部分存储指向下一个节点的引用(或指针)。最后一个节点的指针通常指向 null,表示链表结束。

假设我们要创建一个链表来存储一系列整数。链表的第一个节点(头节点)存储数字 1,第二个节点存储数字 2,以此类推。

plaintext 复制代码
节点1 -> 节点2 -> 节点3 -> ... -> 节点n
|     |     |           |         |
|-----|-----|-----------|---------|
| 1   | →   |     2     | → ... → |
| next|     | next     |         |

在这个例子中,每个节点包含两个部分:

  • 数据部分(如 1, 2, ... n)
  • 指向下一个节点的指针(next)

优点

  1. 动态大小:链表可以根据需要动态地增加或减少节点,无需预先分配固定大小的内存。
  2. 高效插入与删除:在链表中插入或删除一个元素,只需更改相邻节点之间的指针,时间复杂度可以达到 O(1),而数组中插入或删除元素可能需要移动大量元素。
  3. 内存利用率高:因为链表只在需要时分配节点,不会造成内存空间的浪费,特别适合存储大量但不确定数量的数据。

缺点

  1. 访问速度较慢:访问链表中的某个元素需要从头节点开始,逐个遍历直至找到目标节点,时间复杂度为 O(n),而数组可以通过索引直接访问,时间复杂度为 O(1)。
  2. 额外的存储开销:每个节点除了存储数据外,还需额外的空间来存储指向下一个节点的指针。
  3. 不支持随机访问:链表不能像数组那样通过索引直接访问元素,降低了某些操作的效率。
  4. 内存碎片:频繁的插入和删除可能导致内存空间碎片化。

综上所述,链表结构在处理需要频繁插入和删除操作,且不需要快速随机访问数据的场景下更为高效,但在需要快速访问特定位置数据的应用中,其性能不如数组。

编码实战

demo

demo地址: https://tiandisheng.top/utils/music-list

核心代码

js 复制代码
// LinkedList.js
class ListNode {
  data: any;
  next: any;
  constructor(data) {
    this.data = data;
    this.next = null;
  }
}

class LinkedList {
  head: any;
  constructor() {
    this.head = null;
  }

  // 返回链表长度
  length() {
    let current = this.head;
    let count = 0;
    while (current) {
      count++;
      current = current.next;
    }
    return count;
  }

  /**
   * 新增
   */
  append(data) {
    const newNode = new ListNode(data);
    if (!this.head) {
      this.head = newNode;
    } else {
      let current = this.head;
      while (current.next) {
        current = current.next;
      }
      current.next = newNode;
    }
  }

  /**
   * 删除
   */
  remove(data) {
    if (!this.head) return;
    if (this.head.data === data) {
      this.head = this.head.next;
      return;
    }
    let current = this.head;
    while (current.next && current.next.data !== data) {
      current = current.next;
    }
    if (current.next) {
      current.next = current.next.next;
    }
  }

  /**
   * 在指定位置插入数据
   */
  insertAt(data, position) {
    if (position < 0 || position > this.length()) {
      console.error('Insert position is out of range.');
      return;
    }

    const newNode = new ListNode(data);
    if (position === 0) {
      newNode.next = this.head;
      this.head = newNode;
    } else {
      let current = this.head;
      let prev = null;
      for (let i = 0; i < position; i++) {
        prev = current;
        current = current.next;
      }
      newNode.next = current;
      prev.next = newNode;
    }
  }

  /**
   * 置顶
   * @param {*} data 要置顶的节点的数据
   */
  moveToTop(data) {
    if (!this.head) return; // 链表为空时无需操作

    // 如果头节点就是要置顶的节点,则无需操作
    if (this.head.data === data) return;

    // 首先尝试找到待置顶节点及其前驱节点
    let current = this.head;
    let prev = null;

    while (current && current.data !== data) {
      prev = current;
      current = current.next;
    }

    if (!current) return; // 数据不存在于链表中

    // 如果找到了待置顶的节点
    if (prev) {
      // 从原位置删除节点
      prev.next = current.next;
    } else {
      // 如果是头节点,则直接更新head
      this.head = current.next;
    }

    // 插入到头部
    current.next = this.head;
    this.head = current;
  }

  toArray() {
    let current = this.head;
    const array: any[] = [];
    while (current) {
      array.push(current.data);
      current = current.next;
    }
    return array;
  }

  /**
   * @function 将"数组结构"的数据转换为"链表结构"的数据
   */
  arrayToLinkList(arrayData: any[]) {
    const newLinkList = new LinkedList();
    arrayData.forEach((i) => {
      newLinkList.append(i);
    });
    return newLinkList;
  }

  /**
   * @function 将当前链表数据转换为一个新的链表副本
   */
  createCopy() {
    return this.arrayToLinkList(this.toArray());
  }
}

export { LinkedList };
相关推荐
lmy_loveF3 分钟前
go 切换go version 版本
开发语言·后端·golang
小白快快跑哦7 分钟前
python-字符串全解(四):字符串方法
开发语言·python·字符串
mONESY32 分钟前
让大模型「听话」输出 JSON:LangChain OutputParser 结构化输出实战,从手写正则到 Zod Schema 四层进化
javascript
大模型丫丫36 分钟前
LangGraph + MCP(Model Context Protocol)完整讲解
java·开发语言·数据库
凡泰AI39 分钟前
小程序容器技术解析:一个能够同时在多端APP运行同一个小程序的SDK需要关注哪些内容?
开发语言·小程序·mpaas·uni·技术实践
小灰灰搞电子39 分钟前
Rust+Slint 实现抽屉式侧边栏源码分享
开发语言·rust·侧边栏
quantdash_cc43 分钟前
如何设计高效率的批量股票数据获取程序?QuantDash Python SDK 全市场行情拉取实战指南
开发语言·python·数据分析·量化交易·股票数据·quantdash
Jul1en_1 小时前
Matt 与 Uncle Bob 的播客访谈有感
开发语言·经验分享·笔记·ai·开源·github·ai编程
玖玥拾1 小时前
Lua 基础语法(五)Unity xLua基础配置与 C# 访问 Lua
开发语言·unity·c#·lua
淼澄研学1 小时前
Sonos空间音频技术解析与Python本地API控制实操
开发语言·python·音视频