Priority Queue
是一種特殊類型的資料結構,其中每個元素都有一個與之關聯的優先級或權重,並且根據其優先級來決定存取和刪除元素的順序。不同於一般的佇列(Queue),Priority Queue
不一定是先進先出(FIFO)的,而是根據元素的優先級來決定處理的順序。
優先級該如何比較? 優先級通常根據事先定義的比較函數(Comparator)或物件本身的內部屬性來決定,並且越小或越大的元素視為優先級越高,取決於具體的實現方式。
Priority Queue
在許多應用場景中都是非常有用的,例如作業系統中的進程調度、路由協議中的路徑選擇、圖形算法中的最小生成樹、現實生活中醫院急診室中的病人。
Priority Queue
中,並根據其優先級進行排序。Priority Queue
中具有最高/最低優先級的元素,並返回其值。Priority Queue
中具有最高/最低優先級的元素的值,但不刪除它。Priority Queue
中修改某個元素的優先級,並重新排序。實現 | 插入Enqueue | 刪除Dequeue |
---|---|---|
Max Heap | ||
LinkedList | ||
Array |
依序實現以下功能:
首先先定義一個 PriorityQueueNode
類別,這個類別有兩個屬性 value
和 priority
,分別代表節點的值和優先級。
接著,定義了 PriorityQueue
類別,這個類別有一個屬性 queue
,它使用一個陣列來實現優先佇列。在這個優先佇列中,元素是按照優先級排序的,優先級高的元素會被先取出。
// 定義優先佇列的節點類別
class PriorityQueueNode {
constructor(value, priority) {
this.value = value; // 節點的值
this.priority = priority; // 節點的優先級
}
}
// 定義優先佇列類別
class PriorityQueue {
constructor() {
this.queue = []; // 使用陣列來實現優先佇列
}
}
接下來新增的是 PriorityQueue
類別中的 enqueue
方法,用於向優先佇列中插入一個新的節點。
這個方法接受兩個參數:value
表示要插入節點的值,priority
表示要插入節點的優先級。該方法根據優先級的大小將新節點插入到適當位置。
// 插入操作enqueue
enqueue(value, priority) {
const newNode = new PriorityQueueNode(value, priority); // 創建新的節點
let inserted = false; // 用於標記是否已經插入新節點
for (let i = 0; i < this.queue.length; i++) {
if (newNode.priority < this.queue[i].priority) { // 比較優先級
this.queue.splice(i, 0, newNode); // 將新節點插入到適當位置
inserted = true;
break;
}
}
if (!inserted) {
this.queue.push(newNode); // 若未插入,則將新節點添加到佇列末尾
}
}
從優先佇列中取出優先級最高的節點。如果佇列為空,則返回 null
。
// 出隊操作dequeue
dequeue() {
if (this.isEmpty()) {
return null; // 若佇列為空,則返回null
}
return this.queue.shift(); // 移除佇列頭部的節點並返回其值
}
查看優先佇列中優先級最高的節點的值,但不將其從佇列中移除。如果佇列為空,則返回 null
。
peek() {
if (this.isEmpty()) {
return null;
}
return this.queue[0];
}
// 定義優先佇列的節點類別
class PriorityQueueNode {
constructor(value, priority) {
this.value = value; // 節點的值
this.priority = priority; // 節點的優先級
}
}
// 定義優先佇列類別
class PriorityQueue {
constructor() {
this.queue = []; // 使用陣列來實現優先佇列
}
// 插入操作enqueue
enqueue(value, priority) {
const newNode = new PriorityQueueNode(value, priority); // 創建新的節點
let inserted = false; // 用於標記是否已經插入新節點
for (let i = 0; i < this.queue.length; i++) {
if (newNode.priority < this.queue[i].priority) { // 比較優先級
this.queue.splice(i, 0, newNode); // 將新節點插入到適當位置
inserted = true;
break;
}
}
if (!inserted) {
this.queue.push(newNode); // 若未插入,則將新節點添加到佇列末尾
}
}
// 出隊操作dequeue
dequeue() {
if (this.isEmpty()) {
return null; // 若佇列為空,則返回null
}
return this.queue.shift(); // 移除佇列頭部的節點並返回其值
}
// 查詢優先級最高元素
peek() {
if (this.isEmpty()) {
return null;
}
return this.queue[0][0];
}
// 更新元素
update(value, newPriority) {
let found = false; // 用來標記是否找到待更新的元素
// 在佇列中尋找待更新的元素
for (let i = 0; i < this.queue.length; i++) {
if (this.queue[i].value === value) {
// 找到待更新的元素
this.queue[i].priority = newPriority; // 更新優先級
found = true; // 標記已找到
break; // 停止尋找
}
}
if (!found) {
// 若未找到待更新的元素,則返回null
return null;
}
// 將佇列重新排序以維持優先級順序
this.queue.sort((a, b) => a.priority - b.priority);
}
// 判斷佇列是否為空
isEmpty() {
return this.queue.length === 0;
}
}
依序實現以下功能:
在堆積(Heap)中如何找到節點的child node
或是parent node
索引位置呢?
查看以下範例,範例為一顆Max Heap
:
假設要求節點6的 child node
以及 parent node
索引位置該怎麼尋找?讓我們將以上公式帶入
// child node
let x = 1;
let childNode = [2x+1, 2x+2] // [3, 4]
// parent node
let x = 1;
let parentNode = Math.floor((1-1)/2) // 0
Error: Expected an atom of EOF but received ordinary at position 14: `ENQUEUE(value,↱ priority): `
class Node {
constructor(value, priority) {
this.value = value;
this.priority = priority;
}
}
class PriorityQueue {
constructor() {
this.values = []
}
enqueue(value, priority) {
let node = new PQNode(value, priority);
if (this.values.length == 0) {
this.values.push(node)
} else {
this.values.push(node);
let newIndex = this.values.length - 1
let parentIndex = Math.floor((newIndex - 1) / 2)
while (parentIndex >= 0 && this.values[newIndex].priority > this.values[parentIndex].priority) {
[this.values[parentIndex], this.values[newIndex]] = [this.values[newIndex], this.values[parentIndex]];
newIndex = parentIndex
parentIndex = Math.floor((newIndex - 1) / 2)
}
}
}
}
Error: Expected an atom of EOF but received ordinary at position 10: `DEQUEUE():↱ if PQ.lengt`
class PriorityQueue {
constructor() {
this.values = []
}
dequeue() {
if (this.isEmpty()) {
return false
}
if (this.values.length == 1) {
return this.values.shift();
} else {
[this.values[0], this.values[this.values.length - 1]] = [this.values[this.values.length - 1], this.values[0]]
let max = this.values.pop();
this.maxHeapify(0);
return max;
}
}
maxHeapify(i) {
let largest;
let l = i * 2 + 1;
let r = i * 2 + 2;
if (l < this.values.length - 1 && this.values[l].priority > this.values[i].priority) {
largest = l;
} else {
largest = i;
}
if (r <= this.values.length - 1 && this.values[r].priority && this.values[largest].priority) {
largest = r;
}
if (largest != i) {
[this.values[i], this.values[largest]] = [this.values[largest], this.values[i]]
maxHeapify(largest);
}
}
isEmpty() {
return this.values.length == 0;
}
}
class Node {
constructor(value, priority) {
this.value = value;
this.priority = priority;
}
}
class PriorityQueue {
constructor() {
this.values = []
}
enqueue(value, priority) {
let node = new Node(value, priority);
if (this.values.length == 0) {
this.values.push(node)
} else {
this.values.push(node);
let newIndex = this.values.length - 1
let parentIndex = Math.floor((newIndex - 1) / 2)
while (parentIndex >= 0 && this.values[newIndex].priority > this.values[parentIndex].priority) {
[this.values[newIndex], this.values[parentIndex]] = [this.values[parentIndex], this.values[newIndex]];
newIndex = parentIndex
parentIndex = Math.floor((newIndex - 1) / 2)
}
}
}
dequeue() {
if (this.isEmpty()) {
return false
}
if (this.values.length == 1) {
return this.values.shift();
} else {
[this.values[0], this.values[this.values.length - 1]] = [this.values[this.values.length - 1], this.values[0]]
let max = this.values.pop();
this.maxHeapify(0);
return max;
}
}
maxHeapify(i) {
let largest;
let l = i * 2 + 1;
let r = i * 2 + 2;
if (l < this.values.length - 1 && this.values[l].priority > this.values[i].priority) {
largest = l;
} else {
largest = i;
}
if (r <= this.values.length - 1 && this.values[r].priority && this.values[largest].priority) {
largest = r;
}
if (largest != i) {
[this.values[i], this.values[largest]] = [this.values[largest], this.values[i]]
maxHeapify(largest);
}
}
peek() {
return this.values[0];
}
isEmpty() {
return this.values.length == 0;
}
}
let pq = new PriorityQueue();
pq.enqueue("A", 1);
pq.enqueue("B", 2);
pq.enqueue("C", 3);
pq.dequeue()
console.log(pq.peek());
console.log(pq.values)
class PriorityQueueNode {
constructor(value, priority) {
this.value = value;
this.priority = priority;
}
}
class PriorityQueue {
constructor() {
this.heap = [];
}
enqueue(value, priority) {
const node = new PriorityQueueNode(value, priority);
this.heap.push(node);
this.bubbleUp(this.heap.length - 1);
}
dequeue() {
const max = this.heap[0];
const end = this.heap.pop();
if (this.heap.length > 0) {
this.heap[0] = end;
this.bubbleDown(0);
}
return max.value;
}
peek() {
return this.heap[0].value;
}
bubbleUp(index) {
const node = this.heap[index];
while (index > 0) {
const parentIndex = Math.floor((index - 1) / 2);
const parent = this.heap[parentIndex];
if (node.priority <= parent.priority) break;
this.heap[index] = parent;
index = parentIndex;
}
this.heap[index] = node;
}
bubbleDown(index) {
const node = this.heap[index];
const length = this.heap.length;
while (true) {
const leftChildIndex = 2 * index + 1;
const rightChildIndex = 2 * index + 2;
let leftChild, rightChild;
let swap = null;
if (leftChildIndex < length) {
leftChild = this.heap[leftChildIndex];
if (leftChild.priority > node.priority) {
swap = leftChildIndex;
}
}
if (rightChildIndex < length) {
rightChild = this.heap[rightChildIndex];
if (
(swap === null && rightChild.priority > node.priority) ||
(swap !== null && rightChild.priority > leftChild.priority)
) {
swap = rightChildIndex;
}
}
if (swap === null) break;
this.heap[index] = this.heap[swap];
index = swap;
}
this.heap[index] = node;
}
}
class PriorityQueueNode {
constructor(value, priority) {
this.value = value;
this.priority = priority;
this.next = null;
}
}
class PriorityQueue {
constructor() {
this.head = null;
}
enqueue(value, priority) {
const newNode = new PriorityQueueNode(value, priority);
if (this.isEmpty()) {
this.head = newNode;
} else if (newNode.priority < this.head.priority) {
newNode.next = this.head;
this.head = newNode;
} else {
let current = this.head;
while (current.next !== null && current.next.priority <= newNode.priority) {
current = current.next;
}
newNode.next = current.next;
current.next = newNode;
}
}
dequeue() {
if (this.isEmpty()) {
return null;
}
const value = this.head.value;
this.head = this.head.next;
return value;
}
peek() {
if (this.isEmpty()) {
return null;
}
return this.head.value;
}
isEmpty() {
return this.head === null;
}
}