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 };
相关推荐
晴天161 小时前
前端 postMessage 使用场景
前端·javascript·网络
zhanghaha13142 小时前
HTML系列教程:14_HTML 表单与输入框 <form>、<input> 零基础详解
java·前端·javascript
傻啦嘿哟7 小时前
某招聘平台爬虫:爬取招聘岗位数据,分析各城市薪资水平
开发语言·爬虫·python
2501_933670797 小时前
2026秋招量化分析岗技能栈:Python、SQL、统计建模、回测项目怎么准备
开发语言·python·sql
李少兄8 小时前
JavaScript 数据类型完全指南
开发语言·javascript·ecmascript
Seoyoneh8 小时前
Agentic Workflow编排架构:云客服从“被动响应”迈向“主动执行”的技术实现
java·开发语言·架构
Tangyuewei9 小时前
Java 写 Agent:模型只是组件
java·开发语言
小玮看世界9 小时前
[Python]从合并区间到传感器融合区:合并区间在传感器区域融合的实际落地
开发语言·python
繁星蓝雨10 小时前
C++的设计与演进大纲(如何从C语言一步步成长为C++)
c语言·开发语言·c++·c++历史·c++演进
Evand J10 小时前
【MATLAB例程,图像降噪滤波9】自适应维纳滑动窗口滤波(Wiener)图像降噪、方法对比,有中文注释
开发语言·图像处理·计算机视觉·matlab·滑动窗口·滤波·降噪