在最小生成树Prim算法中,可以利用最小优先级队列来改善时间复杂度,同时在单源最短路径Dijkstra算法中也同样可以利用这种最小优先级队列来改善算法时间复杂度。实现最小优先级队列可以有很多种方式,比如基于二叉最小堆,或者斐波那契堆等。这里是二叉最小堆的C#实现,原理是根据书上的伪代码来的,但有些地方我做了改进,比如书key值改变,原来书上只能变大,这里取掉了这个限制。同时还提供了根据卫星值来选择元素的功能,下面是代码:///
/// 队列元素包装类
///
/// 实际元素类型
public class QueueElement
{
///
/// Key值
///
public int KeyValue { get; internal set; }
///
/// 实际对象
///
public T Element { get; private set; }
public QueueElement(T Item, int KeyVal)
{
KeyValue = KeyVal;
Element = Item;
}
}
///
/// 最小优先级队列
///
///
public class MinHeapQueue
{
///
/// 队列元素存放,采用List实现.
///
private List> _queueValues = new List>();
///
/// 队列元素数目
///
public int Count
{
get
{
return _queueValues.Count;
}
}
///
/// 获取队列Key值最小的元素
///
///
public T GetMinimum()
{
return _queueValues[0].Element;
}
///
/// 从队列中取出Key值最小的元素
///
///
public T ExtractMin()
{
if (_queueValues.Count <= 0)
{
throw new Exception("队列为空");
}
T theMin = _queueValues[0].Element;
int theTail = Count - 1;
_queueValues[0] = _queueValues[theTail];
_queueValues.RemoveAt(theTail);
MinHeapify(0);
return theMin;
}
///
/// 整理堆元素,保持最小堆特性,这个函数跟DownAdjust功能相同
///
///
public void MinHeapify(int i)
{
int HeapSize = Count;
int theL = HeapL(i);
int theR = HeapR(i);
int theLeast = i;
if (theL < HeapSize && _queueValues[theL].KeyValue < _queueValues[theLeast].KeyValue)
{
theLeast = theL;
}
if (theR < HeapSize && _queueValues[theR].KeyValue < _queueValues[theLeast].KeyValue)
{
theLeast = theR;
}
if (theLeast != i)
{
SwapElement(i, theLeast);
MinHeapify(theLeast);
}
}
///
/// 改变元素key值
///
///
///
public void ChangeKey(Func SelectFunc, int NewKey)
{
int theIndex = -1;
for (int i = 0; i < Count; i++)
{
if (SelectFunc(_queueValues[i].Element) == true)
{
theIndex = i;
break;
}
}
if (theIndex < 0)
{
return;
}
if (_queueValues[theIndex].KeyValue < NewKey)
{
_queueValues[theIndex].KeyValue = NewKey;
DownAdjust(theIndex);
return;
}
if (_queueValues[theIndex].KeyValue > NewKey)
{
_queueValues[theIndex].KeyValue = NewKey;
UpAdjust(theIndex);
return;
}
}
///
/// 沿树根方向整理元素,保持最小堆特性
///
///
private void UpAdjust(int i)
{
int theIndex = i;
int thePIndex = HeapP(theIndex);
while (thePIndex >= 0 && _queueValues[theIndex].KeyValue < _queueValues[thePIndex].KeyValue)
{
SwapElement(thePIndex, theIndex);
theIndex = thePIndex;
thePIndex = HeapP(theIndex);
}
}
///
/// 沿树叶方向整理元素,保持最小堆特性
///
///
private void DownAdjust(int i)
{
int HeapSize = Count;
int theL = HeapL(i);
int theR = HeapR(i);
int theLeast = i;
if (theL < HeapSize && _queueValues[theL].KeyValue < _queueValues[theLeast].KeyValue)
{
theLeast = theL;
}
if (theR < HeapSize && _queueValues[theR].KeyValue < _queueValues[theLeast].KeyValue)
{
theLeast = theR;
}
if (theLeast != i)
{
SwapElement(i, theLeast);
DownAdjust(theLeast);
}
}
///
/// 改变元素key值
///
///
///
public void ChangeKey(int i, int NewKey)
{
int theIndex = i;
if (_queueValues[theIndex].KeyValue > NewKey)
{
_queueValues[theIndex].KeyValue = NewKey;
UpAdjust(theIndex);
return;
}
if (_queueValues[theIndex].KeyValue < NewKey)
{
_queueValues[theIndex].KeyValue = NewKey;
DownAdjust(theIndex);
return;
}
}
///
/// 删除队列元素
///
///
public void HeapDelete(Func SelectFunc)
{
int theIndex = -1;
for (int i = 0; i < Count; i++)
{
if (SelectFunc(_queueValues[i].Element) == true)
{
theIndex = i;
break;
}
}
if (theIndex < 0)
{
return;
}
SwapElement(theIndex, Count - 1);
_queueValues.RemoveAt(Count - 1);
if (theIndex < Count)
{
int theP = HeapP(theIndex);
bool theUp = false;
if (theP >= 0)
{
if (_queueValues[theIndex].KeyValue < _queueValues[theP].KeyValue)
{
UpAdjust(theIndex);
theUp = true;
}
}
if (theUp == false)
{
MinHeapify(theIndex);
}
}
}
///
/// 队列元素交换位置
///
///
///
private void SwapElement(int i, int j)
{
QueueElement theTmp = _queueValues[i];
_queueValues[i] = _queueValues[j];
_queueValues[j] = theTmp;
}
///
/// 将元素插入队列
///
///
///
public void HeapInsert(T Element, int Key)
{
_queueValues.Add(new QueueElement(Element, int.MinValue));
ChangeKey(Count - 1, Key);
}
///
/// 取节点的左孩子节点
///
///
///
private int HeapL(int i)
{
return i * 2 + 1;
}
///
/// 取节点的右孩子节点
///
///
///
private int HeapR(int i)
{
return i * 2 + 2;
}
///
/// 取节点的父节点
///
///
///
private int HeapP(int i)
{
return (i + 1) / 2 - 1;
}
}
需要注意的,我前面有篇基于二叉最大堆的优先级队列算法跟这篇很类似,但上篇算法中有小错误,而这篇算法中的优先级队列已通过测试,没问题。大家可对比一下,看错误在哪里。
Ps:既然到首页,还是加点注释.