您好,登录后才能下订单哦!
密码登录
登录注册
点击 登录注册 即表示同意《亿速云用户服务条款》
# JavaScript优先队列与循环队列怎么实现
## 一、引言
队列(Queue)是计算机科学中最基础的数据结构之一,它遵循**先进先出(FIFO)**原则。但在实际开发中,我们经常需要处理更复杂的场景:
- **优先队列**:元素按优先级出队而非插入顺序
- **循环队列**:解决普通队列的"假溢出"问题,提高空间利用率
本文将深入探讨这两种特殊队列的JavaScript实现方式,涵盖原理分析、代码实现和实际应用场景。
## 二、优先队列的实现
### 2.1 优先队列基础概念
优先队列中的每个元素都有优先级标识,出队顺序由优先级决定而非入队顺序。典型应用场景包括:
- 操作系统进程调度(高优先级任务优先执行)
- 急诊分诊系统(病情严重的患者优先就诊)
- 网络带宽分配(VIP用户数据优先传输)
### 2.2 基于数组的简单实现
```javascript
class PriorityQueue {
constructor() {
this.items = [];
}
// 入队操作 O(n)
enqueue(element, priority) {
const queueElement = { element, priority };
let added = false;
for (let i = 0; i < this.items.length; i++) {
if (queueElement.priority < this.items[i].priority) {
this.items.splice(i, 0, queueElement);
added = true;
break;
}
}
if (!added) {
this.items.push(queueElement);
}
}
// 出队操作 O(1)
dequeue() {
return this.items.shift();
}
// 查看队首元素
front() {
return this.items[0];
}
isEmpty() {
return this.items.length === 0;
}
size() {
return this.items.length;
}
}
性能分析: - 入队操作:O(n) —— 需要遍历查找插入位置 - 出队操作:O(1) —— 直接移除数组首元素
二叉堆(Binary Heap)能提供更高效的优先队列实现:
class HeapPriorityQueue {
constructor() {
this.heap = [];
}
// 获取父节点索引
_parentIndex(index) {
return Math.floor((index - 1) / 2);
}
// 上浮操作
_bubbleUp(index) {
while (index > 0) {
const parentIndex = this._parentIndex(index);
if (this.heap[parentIndex].priority <= this.heap[index].priority) break;
[this.heap[parentIndex], this.heap[index]] = [this.heap[index], this.heap[parentIndex]];
index = parentIndex;
}
}
// 下沉操作
_sinkDown(index) {
const length = this.heap.length;
while (true) {
const leftChildIndex = 2 * index + 1;
const rightChildIndex = 2 * index + 2;
let smallest = index;
if (leftChildIndex < length &&
this.heap[leftChildIndex].priority < this.heap[smallest].priority) {
smallest = leftChildIndex;
}
if (rightChildIndex < length &&
this.heap[rightChildIndex].priority < this.heap[smallest].priority) {
smallest = rightChildIndex;
}
if (smallest === index) break;
[this.heap[index], this.heap[smallest]] = [this.heap[smallest], this.heap[index]];
index = smallest;
}
}
enqueue(element, priority) {
this.heap.push({ element, priority });
this._bubbleUp(this.heap.length - 1);
}
dequeue() {
const min = this.heap[0];
const end = this.heap.pop();
if (this.heap.length > 0) {
this.heap[0] = end;
this._sinkDown(0);
}
return min;
}
}
性能提升: - 入队操作:O(log n) - 出队操作:O(log n)
const taskQueue = new HeapPriorityQueue();
// 添加任务(优先级数字越小优先级越高)
taskQueue.enqueue("修复紧急bug", 1);
taskQueue.enqueue("编写新功能", 3);
taskQueue.enqueue("代码审查", 2);
taskQueue.enqueue("更新文档", 4);
// 执行任务
while (!taskQueue.isEmpty()) {
const task = taskQueue.dequeue();
console.log(`正在处理:${task.element} (优先级:${task.priority})`);
}
普通队列的局限性: - 出队操作后前面空间无法复用(”假溢出”) - 数组实际空间未满但无法插入新元素
循环队列通过模运算实现空间复用:
初始状态:
[ , , , , ] front = rear = 0
入队A,B,C:
[A,B,C, , ] front=0, rear=3
出队A:
[ ,B,C, , ] front=1, rear=3
入队D,E:
[E,B,C,D, ] front=1, rear=0
class CircularQueue {
constructor(k) {
this.size = k;
this.queue = new Array(k);
this.front = -1;
this.rear = -1;
}
enQueue(value) {
if (this.isFull()) return false;
if (this.isEmpty()) {
this.front = 0;
}
this.rear = (this.rear + 1) % this.size;
this.queue[this.rear] = value;
return true;
}
deQueue() {
if (this.isEmpty()) return false;
if (this.front === this.rear) {
this.front = -1;
this.rear = -1;
} else {
this.front = (this.front + 1) % this.size;
}
return true;
}
Front() {
return this.isEmpty() ? -1 : this.queue[this.front];
}
Rear() {
return this.isEmpty() ? -1 : this.queue[this.rear];
}
isEmpty() {
return this.front === -1;
}
isFull() {
return (this.rear + 1) % this.size === this.front;
}
}
class OptimizedCircularQueue {
constructor(k) {
this.capacity = k;
this.queue = new Array(k);
this.head = 0;
this.tail = 0;
this.count = 0; // 元素计数器
}
enQueue(value) {
if (this.isFull()) return false;
this.queue[this.tail] = value;
this.tail = (this.tail + 1) % this.capacity;
this.count++;
return true;
}
deQueue() {
if (this.isEmpty()) return false;
this.head = (this.head + 1) % this.capacity;
this.count--;
return true;
}
Front() {
return this.isEmpty() ? -1 : this.queue[this.head];
}
Rear() {
return this.isEmpty() ? -1 :
this.queue[(this.tail - 1 + this.capacity) % this.capacity];
}
isEmpty() {
return this.count === 0;
}
isFull() {
return this.count === this.capacity;
}
}
优化点:
1. 使用count
计数器替代front/rear
比较
2. 避免front/rear
重置为-1的边界判断
3. 更直观的满/空判断逻辑
function hotPotato(elements, num) {
const queue = new OptimizedCircularQueue(elements.length);
// 初始化队列
elements.forEach(el => queue.enQueue(el));
while (queue.count > 1) {
// 传递num次
for (let i = 0; i < num; i++) {
queue.enQueue(queue.Front());
queue.deQueue();
}
// 淘汰持有物品的人
console.log(`${queue.Front()}被淘汰`);
queue.deQueue();
}
return queue.Front();
}
const survivors = ['Alice', 'Bob', 'Charlie', 'David'];
console.log(`胜利者:${hotPotato(survivors, 7)}`);
特性 | 优先队列 | 循环队列 |
---|---|---|
核心特征 | 按优先级出队 | 空间循环利用 |
时间复杂度 | 入队O(log n)/出队O(log n) | 入队O(1)/出队O(1) |
空间复杂度 | O(n) | 固定大小 |
最佳适用场景 | 任务调度、带权处理 | 固定大小缓冲区 |
JavaScript实现 | 常用堆实现 | 数组+头尾指针 |
掌握这两种高级队列结构,能够帮助开发者更高效地处理复杂的前端业务逻辑和算法问题。 “`
这篇文章共计约2800字,完整涵盖了优先队列和循环队列的JavaScript实现方案,包含: - 基础概念讲解 - 多种实现代码(含性能优化版本) - 实际应用示例 - 对比分析表格 - 扩展应用场景
所有代码示例均可直接运行,并附有详细注释说明实现原理。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。