优先队列(Priority Queue)是一种特殊的队列,它的出队顺序与普通队列不同,而是根据元素的优先级来决定的。具有高优先级的元素先出队列,相同优先级的元素按照其在队列中的顺序出队列。
优先队列的实现方式有很多种,其中一种比较常见的方式是使用堆(Heap)来实现。堆是一种完全二叉树,它的每个节点的值都比其子节点的值小(或大),这种特性使得堆可以用来实现优先队列。
下面我们以 JavaScript 为例,来看一个基于最小堆实现的优先队列的例子:
class PriorityQueue {
constructor() {
this.heap = [null];
}
// 元素入队列
enqueue(value, priority = 0) {
const node = { value, priority };
this.heap.push(node);
let current = this.heap.length - 1;
let parent = Math.floor(current / 2);
while (parent && this.heap[current].priority < this.heap[parent].priority) {
[this.heap[current], this.heap[parent]] = [this.heap[parent], this.heap[current]];
current = parent;
parent = Math.floor(current / 2);
}
}
// 获取队列中优先级最高的元素
dequeue() {
const min = this.heap[1];
if (this.heap.length === 2) {
this.heap = [null];
} else {
this.heap[1] = this.heap.pop();
let current = 1;
let [left, right] = [current * 2, current * 2 + 1];
let child = this.heap[right] && this.heap[right].priority < this.heap[left].priority ? right : left;
while (child && this.heap[current].priority > this.heap[child].priority) {
[this.heap[current], this.heap[child]] = [this.heap[child], this.heap[current]];
current = child;
[left, right] = [current * 2, current * 2 + 1];
child = this.heap[right] && this.heap[right].priority < this.heap[left].priority ? right : left;
}
}
return min ? min.value : null;
}
// 判断队列是否为空
isEmpty() {
return this.heap.length === 1;
}
// 获取队列长度
size() {
return this.heap.length - 1;
}
}
// 创建一个新的优先队列
const priorityQueue = new PriorityQueue();
// 元素入队列
priorityQueue.enqueue('A', 1);
priorityQueue.enqueue('B', 2);
priorityQueue.enqueue('C', 3);
// 获取队列中优先级最高的元素
console.log(priorityQueue.dequeue()); // 'A'
// 元素入队列
priorityQueue.enqueue('D', 2);
// 获取队列中优先级最高的元素
console.log(priorityQueue.dequeue()); // 'B'
// 获取队列中优先级最高的元素
console.log(priorityQueue.dequeue()); // 'C'
// 获取队列中优先级最高的元素
console.log(priorityQueue.dequeue()); // 'D'
优先队列可以应用于很多场景,例如操作系统中的进程调度、网络数据包的传输等。举个例子,假设我们要实现一个任务调度器,其中包含多个不同优先级的任务,需要按照优先级高低来执行。我们可以使用优先队列来实现这个功能。
class Task {
constructor(id, priority) {
this.id = id;
this.priority = priority;
}
}
class TaskScheduler {
constructor() {
this.priorityQueue = new PriorityQueue();
this.currentTask = null;
this.timer = null;
}
addTask(task) {
this.priorityQueue.enqueue(task, task.priority);
this.scheduleTask();
}
scheduleTask() {
if (this.currentTask) {
return;
}
const nextTask = this.priorityQueue.dequeue();
if (!nextTask) {
return;
}
this.currentTask = nextTask.value;
console.log(`Start executing task ${this.currentTask.id} with priority ${this.currentTask.priority}`);
const executionTime = Math.random() * 5000;
this.timer = setTimeout(() => {
console.log(`Finish executing task ${this.currentTask.id}`);
this.currentTask = null;
this.scheduleTask();
}, executionTime);
}
}
const taskScheduler = new TaskScheduler();
taskScheduler.addTask(new Task(1, 3));
taskScheduler.addTask(new Task(2, 1));
taskScheduler.addTask(new Task(3, 2));
在上面的例子中,我们创建了一个任务调度器 TaskScheduler,其中包含一个优先队列 priorityQueue。当用户添加任务时,我们将任务入队列,并立即调用 scheduleTask 方法来开始执行任务。
scheduleTask 方法首先检查当前是否有正在执行的任务,如果有,则不进行任何操作。否则,它从优先队列中获取下一个要执行的任务,并将其作为当前任务。然后,它随机生成一个执行时间,模拟任务的执行过程。一旦任务完成,它将当前任务设置为 null,然后递归调用 scheduleTask 方法来继续执行下一个任务。
在上面的例子中,我们添加了三个任务,它们的优先级分别为 3、1 和 2。由于优先级为 1 的任务是最高优先级的任务,因此它将首先执行。一旦该任务完成,调度器将自动继续执行剩余的任务,直到队列为空为止。