JavaScript优先队列实现:二叉堆代码示例与复杂度分析

2025/3/15
本文详细介绍了优先队列的概念及其实现方式,重点展示了如何使用二叉堆(最小堆)在JavaScript中实现优先队列,并提供了完整的代码示例和复杂度分析。
一个二叉堆的示意图,展示最小堆的结构和元素排列

优先队列(Priority Queue)是一种特殊的队列,其中每个元素都有一个优先级。与普通队列不同,优先队列中的元素按照优先级顺序出队,而不是按照入队顺序。实现优先队列的常见方式包括使用二叉堆(Binary Heap)或平衡二叉搜索树(如AVL树、红黑树)。

下面是一个使用二叉堆(最小堆)实现优先队列的示例代码:

class PriorityQueue {
  constructor(comparator = (a, b) => a - b) {
    this.heap = [];
    this.comparator = comparator;
  }

  // 获取父节点索引
  parent(index) {
    return Math.floor((index - 1) / 2);
  }

  // 获取左子节点索引
  leftChild(index) {
    return 2 * index + 1;
  }

  // 获取右子节点索引
  rightChild(index) {
    return 2 * index + 2;
  }

  // 交换两个元素
  swap(i, j) {
    [this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
  }

  // 上浮操作
  bubbleUp(index) {
    while (index > 0 && this.comparator(this.heap[index], this.heap[this.parent(index)]) < 0) {
      this.swap(index, this.parent(index));
      index = this.parent(index);
    }
  }

  // 下沉操作
  bubbleDown(index) {
    let minIndex = index;
    const left = this.leftChild(index);
    const right = this.rightChild(index);

    if (left < this.heap.length && this.comparator(this.heap[left], this.heap[minIndex]) < 0) {
      minIndex = left;
    }

    if (right < this.heap.length && this.comparator(this.heap[right], this.heap[minIndex]) < 0) {
      minIndex = right;
    }

    if (index !== minIndex) {
      this.swap(index, minIndex);
      this.bubbleDown(minIndex);
    }
  }

  // 插入元素
  push(value) {
    this.heap.push(value);
    this.bubbleUp(this.heap.length - 1);
  }

  // 移除并返回优先级最高的元素
  pop() {
    if (this.isEmpty()) {
      return null;
    }
    const result = this.heap[0];
    const last = this.heap.pop();
    if (this.heap.length > 0) {
      this.heap[0] = last;
      this.bubbleDown(0);
    }
    return result;
  }

  // 查看优先级最高的元素
  peek() {
    return this.isEmpty() ? null : this.heap[0];
  }

  // 判断队列是否为空
  isEmpty() {
    return this.heap.length === 0;
  }

  // 获取队列大小
  size() {
    return this.heap.length;
  }
}

// 示例用法
const pq = new PriorityQueue();
pq.push(5);
pq.push(3);
pq.push(8);
pq.push(1);

console.log(pq.pop()); // 输出: 1
console.log(pq.pop()); // 输出: 3
console.log(pq.pop()); // 输出: 5
console.log(pq.pop()); // 输出: 8

代码说明:

  1. 构造函数PriorityQueue 类接受一个可选的比较函数 comparator,默认情况下使用 (a, b) => a - b,即最小堆。
  2. 插入操作 (push):将新元素插入到堆的末尾,然后通过 bubbleUp 操作将其上浮到正确的位置。
  3. 移除操作 (pop):移除并返回堆顶元素(优先级最高的元素),然后将堆的最后一个元素移动到堆顶,并通过 bubbleDown 操作将其下沉到正确的位置。
  4. 查看操作 (peek):返回堆顶元素,但不移除它。
  5. 辅助方法parentleftChildrightChildswap 等方法用于操作堆结构。

复杂度分析:

  • 插入操作 (push):时间复杂度为 O(log n),因为需要上浮操作。
  • 移除操作 (pop):时间复杂度为 O(log n),因为需要下沉操作。
  • 查看操作 (peek):时间复杂度为 O(1),因为直接访问堆顶元素。

应用场景:

优先队列常用于需要动态维护一组元素并快速获取最高优先级元素的场景,如任务调度、Dijkstra算法、Huffman编码等。

如果你有更复杂的需求,比如需要支持动态更新优先级,可以考虑使用更复杂的数据结构,如斐波那契堆。

上次更新:

相关文章

npx完全指南:前端开发必备工具详解 | 20年架构师深度解析

本文由20年前端架构师深入解析npx工具,涵盖其核心功能、优势、高级用法、最佳实践及与npm/yarn的区别比较,帮助开发者掌握这一现代前端开发利器。

·前端开发

Astro 静态站点生成器:构建高性能网站的最佳选择

Astro 是一个专注于构建快速、轻量级网站的静态站点生成器,支持多种前端框架,采用岛屿架构减少 JavaScript 加载,提升性能。

·前端开发

MySQL JSON数据类型支持与使用指南 | 详细解析与示例

本文详细解析了MySQL从5.7版本开始支持的JSON数据类型,包括版本支持、创建JSON字段、插入与查询JSON数据、修改JSON数据、生成JSON、索引优化、性能与应用场景、注意事项及示例全流程。

·后端开发

Weex 跨平台移动开发框架:核心特性与使用指南

Weex 是由阿里巴巴开源的跨平台移动开发框架,支持使用 Vue.js 或 Rax 构建高性能的 iOS、Android 和 Web 应用。本文详细解析了 Weex 的核心特性、架构、工作流程、组件和模块、开发工具、优缺点、应用场景及未来发展。

·前端开发

ECharts 与 DataV 数据可视化工具对比分析 | 选择指南

本文详细对比了 ECharts 和 DataV 两个常用的数据可视化工具,包括它们的设计目标、优缺点、使用场景和技术栈,帮助读者根据具体需求选择合适的工具。

·前端开发

前端部署后通知用户刷新页面的常见方案 | 单页应用更新提示

本文介绍了在前端部署后通知用户刷新页面的几种常见方案,包括WebSocket实时通知、轮询检查版本、Service Worker版本控制、版本号对比、自动刷新、使用框架内置功能以及第三方库。每种方案的优缺点和示例代码均有详细说明。

·前端开发

TypeScript 映射类型常见问题与解决方案 | 提升代码维护性

本文探讨了在使用 TypeScript 时,映射类型的不当使用可能导致的问题,如代码难以维护、类型推断不准确或性能问题,并提供了相应的解决方案和最佳实践。

·编程语言

TypeScript 交叉类型与联合类型:区别与最佳实践

本文详细解释了 TypeScript 中交叉类型(Intersection Types)和联合类型(Union Types)的区别,提供了使用场景、类型守卫、避免过度使用交叉类型的建议,以及如何通过工具辅助解决混淆问题。

·编程语言

TypeScript 类继承中的常见类型问题及解决方案 | TypeScript 开发指南

本文详细探讨了在 TypeScript 中使用类继承时可能遇到的常见类型问题,包括类型兼容性、构造函数、方法重写、访问修饰符、泛型类继承、抽象类以及类型断言等问题,并提供了相应的解决方案和代码示例。

·编程语言

TypeScript 函数重载:常见问题与解决方案

本文探讨了 TypeScript 中函数重载的常见问题,包括签名与实际实现不匹配、重载签名过多、与泛型结合时的类型推断问题等,并提供了相应的解决方案。

·编程语言