最小优先级队列(基于最小二叉堆算法)

在最小生成树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:既然到首页,还是加点注释.

 

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值