优先队列(Priority Queue)是一种抽象数据类型,元素按优先级出队而非插入顺序。每次出队操作会移除并返回优先级最高(或最低)的元素,具体取决于实现方式。
优先队列的实现方式是基于二叉堆,结构是完全二叉树的结构,
时间复杂度:插入(O(log n)
)、删除(O(log n)
)、获取队首(O(1)
),
完全二叉树的结构:
能够看到,每一个节点都有两个子节点,其中子节点跟父节点的关系可以用一个公式来表达:
父节点=(子节点-1)/2
左子节点=(父节点*2)+1
右子节点=(父节点*2)+2
比如0节点的两个子节点是1节点和2节点,1节点和2节点的父节点是0节点。
接下来上代码详细讲解:
我用C#代码举例:
//优先队列类的泛型需要执定继承IComparable接口,来确保队列内的数据有比较函数
public class PriorityQueue<T> where T : IComparable<T>
比较重要的字段展示:
//这里用list来存数据,只是为了方便演示
private List<T> data;
//构造里创建对应类型的list列表
public PriorityQueue()
{
data = new List<T>();
}
很重要的进队接口:
//进队函数
public void Enqueue(T item)
{
data.Add(item);
//数据列表当前最后一个数据的index
int dataLastIndex = data.Count - 1;
while (dataLastIndex > 0)
{
//找父节点
int parentIndex = (dataLastIndex - 1) / 2;
//CompareTo的值是为了排序从小到大,》0说名父节点更优
if (data[dataLastIndex].CompareTo(data[parentIndex]) >= 0) break;
// 当前的数据比父节点的更小, 需要交换父节点数据
T tmp = data[dataLastIndex];
data[dataLastIndex] = data[parentIndex]; data[parentIndex] = tmp;
//循环将最小的值赋给父节点,直到根节点的值为最小
dataLastIndex = parentIndex;
}
}
很重要的出队接口:
public T Dequeue()
{
//当前数据列表最后一个数据下表
int listMaxIndex = data.Count - 1;
//当前列表第一个数据
T frontItem = data[0];
//最后一个数据放到0下标
data[0] = data[listMaxIndex];
//列表移除最后一个数据
data.RemoveAt(listMaxIndex);
//列表最大的下标更新
--listMaxIndex;
//根节点
int parentIndex = 0;
while (true)
{
//找根节点的左子节点
int leftIndex = parentIndex * 2 + 1;
//如果左节点大于数据列表的最大下标说明还没有子节点
if (leftIndex > listMaxIndex) break;
//找根节点的右子节点 也可以用 parent*2+2
int rightIndex = leftIndex + 1;
//如果右节点下标在列表的最大下标内,
//并且右子节点的数据比左子节点的数据更优(CampareTo<0),
//更换左子节点的下标为右子节点的下标
//这里的leftIndex已经变了含义了,是指在当前子节点里最不优先的节点下标
if (rightIndex <= listMaxIndex &&
data[rightIndex].CompareTo(data[leftIndex]) < 0)
leftIndex = rightIndex;
//如果子节点比父节点的数据更优(CampareTo>0),需要更换数据位置
if (data[parentIndex].CompareTo(data[leftIndex]) <= 0) break;
T tmp = data[parentIndex];
data[parentIndex] = data[leftIndex];
data[leftIndex] = tmp;
//这时候更新父节点的下标,
//以子节点为父节点重复循环内的操作,确保整个队列的数据位置正确
parentIndex = leftIndex;
}
return frontItem;
}
优先队列比较核心的两个接口大概就是这样的,实现就是按照子节点父节点的方式,去使得整个队列的顺序是按照指定的比较方法去排列,保证每一次进队操作,在对应的子父节点关系里的位置确保正确,每一次出队的元素都是整个队列里的最大值或者最小值。