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