Unity–优先级队列
优先级队列是一种抽象数据类型,类似于常规队列或栈,但它每个元素都有一个与之关联的优先级。在优先级队列中,元素的出队顺序不是简单的先进先出(FIFO),而是基于元素的优先级。具有最高优先级的元素会先出队,而优先级较低的元素会后出队。
工作原理和详细过程
- 数据结构:
- 优先级队列通常使用堆(Heap)数据结构来实现,特别是最小堆或最大堆。在最小堆中,堆的根节点包含队列中优先级最高的元素;在最大堆中,根节点包含优先级最低的元素。
- 插入元素(Enqueue):
- 当向优先级队列中插入一个新元素时,该元素被添加到堆的末尾。
- 然后,通过比较新元素与其父节点的优先级,如果新元素的优先级更高(对于最小堆来说是更小),则与父节点交换位置。
- 这个过程一直持续到新元素到达正确的位置,或者它成为根节点。
- 删除元素(Dequeue):
- 当从优先级队列中删除一个元素时,总是移除堆的根节点,因为它包含当前队列中优先级最高的元素。
- 然后,将堆的最后一个元素移动到根节点的位置。
- 接着,通过比较新根节点与其子节点的优先级,如果子节点的优先级更高,则与具有最高优先级的子节点交换位置。
- 这个过程一直持续到新根节点到达正确的位置,或者它成为叶节点。
- 维护堆属性:
- 在插入和删除操作过程中,为了保持堆的属性,可能需要进行多次节点交换,这些操作称为“堆化”(Heapify)。
- 插入时进行的堆化称为“上浮”(Heapify Up),而删除时进行的堆化称为“下沉”(Heapify Down)。
- 时间复杂度:
- 插入和删除操作的时间复杂度通常是O(log n),其中n是队列中元素的数量。这是因为堆的高度是log n,而插入和删除操作最多需要从根节点移动到叶节点。
应用场景
优先级队列在许多场景中非常有用,例如:
- 任务调度:根据任务的优先级来调度它们。
- 数据压缩:例如在霍夫曼编码中使用优先级队列来构建编码树。
- 图算法:例如在Dijkstra算法中使用优先级队列来选择下一个要处理的节点。
小结
优先级队列是一种基于优先级来处理元素的队列,它通过堆数据结构实现,提供了高效的插入和删除操作。它在处理具有不同优先级的任务或数据时非常有用,是许多算法和数据结构的核心组件。
代码实现
基于C#的优先级队列
// **************************************
// description: 优先级队列
// -*- coding : utf-8 -*-
// @Date : 2024/08/22
// @Author : Caron4
// @File : PriotityQueue.cs
// **************************************
using System;
using System.Collections.Generic;
namespace Utils
{
// 优先级队列类,使用堆数据结构实现。
// <typeparam name="T">队列中元素的类型,必须实现IComparable<T>接口。</typeparam>
public class PriotityQueue<T> where T : IComparable<T>
{
private List<T> list = null;
// 获取队列中元素的数量。
public int Count { get => list.Count; }
// 优先级队列的构造函数,队列的初始容量初始化为4
public PriotityQueue(int capacity = 4)
{
list = new List<T>(capacity);
}
// 将元素添加到队列中,并保持队列的优先级顺序。
public void Enqueue(T item)
{
list.Add(item);
HeapifyUp(list.Count - 1);
}
// 从队列中移除并返回具有最高优先级的元素。
public T Dequeue()
{
if (list.Count == 0)
{
return default;
}
T item = list[0];
int endIndex = list.Count - 1;
list[0] = list[endIndex];
list.RemoveAt(endIndex);
--endIndex;
HeapifyDown(0, endIndex);
return item;
}
// 返回队列中具有最高优先级的元素,但不从队列中移除它
public T Peek()
{
return list.Count > 0 ? list[0] : default;
}
// 返回指定元素在队列中的索引
public int IndexOf(T t)
{
return list.IndexOf(t);
}
// 移除指定索引处的元素
public T RemoveAt(int removeIndex)
{
if (list.Count <= removeIndex)
{
return default;
}
T item = list[removeIndex];
int endIndex = list.Count - 1;
list[removeIndex] = list[endIndex];
list.RemoveAt(endIndex);
--endIndex;
if (removeIndex < endIndex)
{
int parentIndex = (removeIndex - 1) / 2;
if (parentIndex > 0 && list[removeIndex].CompareTo(list[parentIndex]) < 0)
{
HeapifyUp(removeIndex);
}
else
{
HeapifyDown(removeIndex, endIndex);
}
}
return item;
}
// 移除指定索引处的元素
public T RemoveItem(T t)
{
int index = IndexOf(t);
return index != -1 ? RemoveAt(index) : default;
}
// 移除队列中指定的元素
public void Clear()
{
list.Clear();
}
//检查队列中是否包含指定元素。
public bool Contains(T t)
{
return list.Contains(t);
}
// 查队列是否为空
public bool IsEmpty()
{
return list.Count == 0;
}
// 将队列转换为列表
public List<T> ToList()
{
return list;
}
// 将队列转换为数组
public T[] ToArray()
{
return list.ToArray();
}
// 用于维护队列的优先级顺序,确保新添加的元素被放置在正确的位置。
void HeapifyUp(int childIndex)
{
int parentIndex = (childIndex - 1) / 2;
while (childIndex > 0 && list[childIndex].CompareTo(list[parentIndex]) < 0)
{
Swap(childIndex, parentIndex);
childIndex = parentIndex;
parentIndex = (childIndex - 1) / 2;
}
}
//用于维护队列的优先级顺序,确保在移除元素后队列仍然保持正确的顺序。
void HeapifyDown(int topIndex, int endIndex)
{
while (true)
{
int minIndex = topIndex;
int childIndex = topIndex * 2 + 1;
if (childIndex <= endIndex && list[childIndex].CompareTo(list[topIndex]) < 0)
minIndex = childIndex;
childIndex = topIndex * 2 + 2;
if (childIndex <= endIndex && list[childIndex].CompareTo(list[minIndex]) < 0)
minIndex = childIndex;
if (topIndex == minIndex) break;
Swap(topIndex, minIndex);
topIndex = minIndex;
}
}
// 交换两个数据
void Swap(int a, int b)
{
T temp = list[a];
list[a] = list[b];
list[b] = temp;
}
}
}
基于c++的优先级队列
#include <iostream>
#include <vector>
#include <algorithm> // for std::swap
#include <stdexcept> // for std::runtime_error
// 优先级队列类
template <typename T>
class PriotityQueue
{
private:
std::vector<T> heap;
public:
// 构造函数
PriotityQueue(int capacity = 4) : heap(capacity) {}
// 获取队列中元素的数量
size_t Count() const { return heap.size(); }
// 将元素添加到队列中
void Enqueue(const T& item)
{
heap.push_back(item);
HeapifyUp(heap.size() - 1);
}
// 从队列中移除并返回具有最高优先级的元素
T Dequeue()
{
if (heap.empty())
throw std::runtime_error("Queue is empty");
T item = heap.front();
std::swap(heap.front(), heap.back());
heap.pop_back();
HeapifyDown(0);
return item;
}
// 返回队列中具有最高优先级的元素
T Peek() const
{
if (heap.empty())
throw std::runtime_error("Queue is empty");
return heap.front();
}
// 返回指定元素在队列中的索引
int IndexOf(const T& t) const
{
auto it = std::find(heap.begin(), heap.end(), t);
if (it != heap.end())
return std::distance(heap.begin(), it);
return -1;
}
// 移除指定索引处的元素
T RemoveAt(int removeIndex)
{
if (removeIndex >= heap.size())
throw std::out_of_range("Index out of range");
T item = heap[removeIndex];
std::swap(heap[removeIndex], heap.back());
heap.pop_back();
if (removeIndex < heap.size())
{
int parentIndex = (removeIndex - 1) / 2;
if (parentIndex >= 0 && heap[removeIndex] < heap[parentIndex])
HeapifyUp(removeIndex);
else
HeapifyDown(removeIndex);
}
return item;
}
// 移除指定元素
void RemoveItem(const T& t)
{
int index = IndexOf(t);
if (index != -1)
RemoveAt(index);
}
// 清空队列
void Clear() { heap.clear(); }
// 检查队列中是否包含指定元素
bool Contains(const T& t) const
{
return std::find(heap.begin(), heap.end(), t) != heap.end();
}
// 检查队列是否为空
bool IsEmpty() const { return heap.empty(); }
// 将队列转换为列表
std::vector<T> ToList() const { return heap; }
// 将队列转换为数组
std::vector<T> ToArray() const { return heap; }
// 用于维护队列的优先级顺序
void HeapifyUp(int childIndex)
{
int parentIndex = (childIndex - 1) / 2;
while (childIndex > 0 && heap[childIndex] < heap[parentIndex])
{
std::swap(heap[childIndex], heap[parentIndex]);
childIndex = parentIndex;
parentIndex = (childIndex - 1) / 2;
}
}
// 用于维护队列的优先级顺序
void HeapifyDown(int topIndex)
{
while (true)
{
int minIndex = topIndex;
int childIndex = topIndex * 2 + 1;
if (childIndex < heap.size() && heap[childIndex] < heap[topIndex])
minIndex = childIndex;
childIndex = topIndex * 2 + 2;
if (childIndex < heap.size() && heap[childIndex] < heap[minIndex])
minIndex = childIndex;
if (topIndex == minIndex) break;
std::swap(heap[topIndex], heap[minIndex]);
topIndex = minIndex;
}
}
};
// 主函数,用于测试优先级队列
int main()
{
PriotityQueue<int> pq;
pq.Enqueue(10);
pq.Enqueue(5);
pq.Enqueue(20);
std::cout << pq.Dequeue() << std::endl; // 应该输出5
std::cout << pq.Dequeue() << std::endl; // 应该输出10
return 0;
}
请注意,C++中的模板类需要在使用时指定类型,例如PriotityQueue<int>
。此外,C++中的异常处理是通过抛出和捕获异常来完成的,所以我添加了std::runtime_error
来处理队列空的情况。其他成员函数的实现也被省略了,如果你使用以上代码的话, 你可以根据上面的模式来实现它们。