using System; using System.Collections; using System.Collections.Generic; namespace IFramework.Queue { /// /// 简易优先级队列 /// /// 队列元素 /// 紧急程度 public class SimplePriorityQueue : IPriorityQueue where TPriority : IComparable { /// /// 简单优先级队列节点 /// private class SimpleNode : GenericPriorityQueueNode { public TItem Data { get; private set; } public SimpleNode(TItem data) { Data = data; } } private const int INITIAL_QUEUE_SIZE = 10; private readonly GenericPriorityQueue _queue; private readonly Dictionary> _itemToNodesCache; private readonly IList _nullNodesCache; #region 构造器 /// /// Ctor /// public SimplePriorityQueue() : this(Comparer.Default, EqualityComparer.Default) { } /// /// Ctor /// /// 用于比较优先级的TPriority值, 默认为Comparer<TPriority>.default public SimplePriorityQueue(IComparer priorityComparer) : this(priorityComparer.Compare, EqualityComparer.Default) { } /// /// Ctor /// /// 用于比较优先级的TPriority值 public SimplePriorityQueue(Comparison priorityComparer) : this(priorityComparer, EqualityComparer.Default) { } /// /// Ctor /// /// 用于比较TItem值是否相等的方法 public SimplePriorityQueue(IEqualityComparer itemEquality) : this(Comparer.Default, itemEquality) { } /// /// Ctor /// /// 用于比较优先级的TPriority值, 默认为Comparer<TPriority>.default /// 用于比较TItem值是否相等的方法 public SimplePriorityQueue(IComparer priorityComparer, IEqualityComparer itemEquality) : this(priorityComparer.Compare, itemEquality) { } /// /// Ctor /// /// 用于比较优先级的TPriority值, 默认为Comparer<TPriority>.default /// 用于比较TItem值是否相等的方法 public SimplePriorityQueue(Comparison priorityComparer, IEqualityComparer itemEquality) { _queue = new GenericPriorityQueue(INITIAL_QUEUE_SIZE, priorityComparer); _itemToNodesCache = new Dictionary>(itemEquality); _nullNodesCache = new List(); } #endregion /// /// 根据所给的item获取队列中的节点 /// /// 匹配的SimpleNode节点 private SimpleNode GetExistingNode(TItem item) { if (item == null) { return _nullNodesCache.Count > 0 ? _nullNodesCache[0] : null; } IList nodes; if (!_itemToNodesCache.TryGetValue(item, out nodes)) { return null; } return nodes[0]; } /// /// 将节点添加到节点缓存
/// O(1) 或 O(log n) ///
/// 需要添加到缓存的节点 private void AddToNodeCache(SimpleNode node) { if (node.Data == null) { _nullNodesCache.Add(node); return; } IList nodes; if (!_itemToNodesCache.TryGetValue(node.Data, out nodes)) { nodes = new List(); _itemToNodesCache[node.Data] = nodes; } nodes.Add(node); } /// /// 将节点从节点缓存中删除
/// O(1) 或 O(log n) 【假设没有重复】 ///
/// 需要从缓存中删除的节点 private void RemoveFromNodeCache(SimpleNode node) { if (node.Data == null) { _nullNodesCache.Remove(node); return; } IList nodes; if (!_itemToNodesCache.TryGetValue(node.Data, out nodes)) { return; } nodes.Remove(node); if (nodes.Count == 0) { _itemToNodesCache.Remove(node.Data); } } /// /// 返回队列里的元素数量
/// O(1) ///
public int count { get { lock (_queue) { return _queue.count; } } } /// /// 队列的第一个元素
/// 如果队列为空,则报错
/// O(1) ///
public TItem first { get { lock (_queue) { if (_queue.count <= 0) { throw new InvalidOperationException("Cannot call .first on an empty queue"); } return _queue.first.Data; } } } /// /// 清空队列里的所有元素
/// O(n) ///
public void Clear() { lock (_queue) { _queue.Clear(); _itemToNodesCache.Clear(); _nullNodesCache.Clear(); } } /// /// 判断当前元素是否在队列中
/// O(1) ///
/// 需要判断的元素 /// 如果队列包含这个元素则返回true 否则为false public bool Contains(TItem item) { lock (_queue) { return item == null ? _nullNodesCache.Count > 0 : _itemToNodesCache.ContainsKey(item); } } /// /// 队列头出队
/// 如果队列是空的则抛出异常
/// O(log n) ///
/// 出队的元素 public TItem Dequeue() { lock (_queue) { if (_queue.count <= 0) { throw new InvalidOperationException("Cannot call Dequeue() on an empty queue"); } SimpleNode node = _queue.Dequeue(); RemoveFromNodeCache(node); return node.Data; } } /// /// 不用lock(_queue)和AddToNodeCache(node)将给定的元素和优先级值入队 /// /// 入队元素 /// 优先级值 /// 入队的SimpleNode对象 private SimpleNode EnqueueNoLockOrCache(TItem item, TPriority priority) { SimpleNode node = new SimpleNode(item); if (_queue.count == _queue.capcity) { _queue.Resize(_queue.capcity * 2 + 1); } _queue.Enqueue(node, priority); return node; } /// /// 将节点元素入队,优先级值小的将会排在队列前面,优先级相同的节点以先进先出排序
/// 队列大小会自动调整,所以不用考虑是否会满
/// 重复添加以及空值都是允许的
/// O(log n) ///
public void Enqueue(TItem item, TPriority priority) { lock (_queue) { IList nodes; if (item == null) { nodes = _nullNodesCache; } else if (!_itemToNodesCache.TryGetValue(item, out nodes)) { nodes = new List(); _itemToNodesCache[item] = nodes; } SimpleNode node = EnqueueNoLockOrCache(item, priority); nodes.Add(node); } } /// /// 不重复入队 /// 将一个不在队列中的item及其优先级入队,优先级值小的会被排在前面,相同的优先级以先进先出排序
/// 队列是自动设置大小的,所以不用关心队列是否是满的
/// 空值是允许的
/// O(log n) ///
/// 成功入队返回true,否则返回false;如果在队列中存在则返回false public bool EnqueueWithoutDuplicates(TItem item, TPriority priority) { lock (_queue) { IList nodes; if (item == null) { if (_nullNodesCache.Count > 0) { return false; } nodes = _nullNodesCache; } else if (_itemToNodesCache.ContainsKey(item)) { return false; } else { nodes = new List(); _itemToNodesCache[item] = nodes; } SimpleNode node = EnqueueNoLockOrCache(item, priority); nodes.Add(node); return true; } } /// /// 从队列中删除给定节点元素,这个节点不一定是队列的头
/// 如果节点并未在队列中,结果是未定义的,如果不确定,则先使用Contains()确认
/// 如果有多个副本加入到队列中,则删除匹配的第一个
/// O(log n) ///
public void Remove(TItem item) { lock (_queue) { SimpleNode removeMe; IList nodes; if (item == null) { if (_nullNodesCache.Count == 0) { throw new InvalidOperationException($"Cannot call Remove() on a node which is not enqueued: {item}"); } removeMe = _nullNodesCache[0]; nodes = _nullNodesCache; } else { if (!_itemToNodesCache.TryGetValue(item, out nodes)) { throw new InvalidOperationException($"Cannot call Remove() on a node which is not enqueued: {item}"); } removeMe = nodes[0]; if (nodes.Count == 1) { _itemToNodesCache.Remove(item); } } _queue.Remove(removeMe); nodes.Remove(removeMe); } } /// /// 更改队列里某个节点的优先级
/// 对不在这个队列中的节点元素调用这个方法会抛出异常
/// 如果item多次入队,只有第一个item会被更新
/// (如果需要同一时间多次入队且可以同时将他们更新,请将你的items放入包装类中来区分它们)
/// O(log n) ///
public void UpdatePriority(TItem item, TPriority priority) { lock (_queue) { SimpleNode updateMe = GetExistingNode(item); if (updateMe == null) { throw new InvalidOperationException("Cannot call UpdatePriority() on a node which is not enqueued: " + item); } _queue.UpdatePriority(updateMe, priority); } } /// /// 返回给定Item的TPriority值 /// 对不在队列中的节点元素调用此方法会抛出异常 /// 如果item多次入队,只有第一个的优先级值会被返回
/// (如果需要同一时间多次入队且可以同时将他们更新,请将你的items放入包装类中来区分它们)
/// O(1) ///
/// 对应的优先级值 public TPriority GetPriority(TItem item) { lock (_queue) { SimpleNode findMe = GetExistingNode(item); if (findMe == null) { throw new InvalidOperationException($"Cannot call GetPriority() on a node which is not enqueued: {item}"); } return findMe.priority; } } #region Try* methods for multithreading /// Get the head of the queue, without removing it (use TryDequeue() for that). /// Useful for multi-threading, where the queue may become empty between calls to Contains() and First /// Returns true if successful, false otherwise /// O(1) public bool TryFirst(out TItem first) { if (_queue.count > 0) { lock (_queue) { if (_queue.count > 0) { first = _queue.first.Data; return true; } } } first = default(TItem); return false; } /// /// Removes the head of the queue (node with minimum priority; ties are broken by order of insertion), and sets it to first. /// Useful for multi-threading, where the queue may become empty between calls to Contains() and Dequeue() /// Returns true if successful; false if queue was empty /// O(log n) /// public bool TryDequeue(out TItem first) { if (_queue.count > 0) { lock (_queue) { if (_queue.count > 0) { SimpleNode node = _queue.Dequeue(); first = node.Data; RemoveFromNodeCache(node); return true; } } } first = default(TItem); return false; } /// /// Attempts to remove an item from the queue. The item does not need to be the head of the queue. /// Useful for multi-threading, where the queue may become empty between calls to Contains() and Remove() /// Returns true if the item was successfully removed, false if it wasn't in the queue. /// If multiple copies of the item are enqueued, only the first one is removed. /// O(log n) /// public bool TryRemove(TItem item) { lock (_queue) { SimpleNode removeMe; IList nodes; if (item == null) { if (_nullNodesCache.Count == 0) { return false; } removeMe = _nullNodesCache[0]; nodes = _nullNodesCache; } else { if (!_itemToNodesCache.TryGetValue(item, out nodes)) { return false; } removeMe = nodes[0]; if (nodes.Count == 1) { _itemToNodesCache.Remove(item); } } _queue.Remove(removeMe); nodes.Remove(removeMe); return true; } } /// /// Call this method to change the priority of an item. /// Useful for multi-threading, where the queue may become empty between calls to Contains() and UpdatePriority() /// If the item is enqueued multiple times, only the first one will be updated. /// (If your requirements are complex enough that you need to enqueue the same item multiple times and be able /// to update all of them, please wrap your items in a wrapper class so they can be distinguished). /// Returns true if the item priority was updated, false otherwise. /// O(log n) /// public bool TryUpdatePriority(TItem item, TPriority priority) { lock (_queue) { SimpleNode updateMe = GetExistingNode(item); if (updateMe == null) { return false; } _queue.UpdatePriority(updateMe, priority); return true; } } /// /// Attempt to get the priority of the given item. /// Useful for multi-threading, where the queue may become empty between calls to Contains() and GetPriority() /// If the item is enqueued multiple times, only the priority of the first will be returned. /// (If your requirements are complex enough that you need to enqueue the same item multiple times and be able /// to query all their priorities, please wrap your items in a wrapper class so they can be distinguished). /// Returns true if the item was found in the queue, false otherwise /// O(1) /// public bool TryGetPriority(TItem item, out TPriority priority) { lock (_queue) { SimpleNode findMe = GetExistingNode(item); if (findMe == null) { priority = default(TPriority); return false; } priority = findMe.priority; return true; } } #endregion /// /// 获取迭代器 /// /// 迭代器 public IEnumerator GetEnumerator() { List queueData = new List(); lock (_queue) { //将队列里的所有item放到单独的列表,因为我们不希望在锁中yield return foreach (var node in _queue) { queueData.Add(node.Data); } } return queueData.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } /// /// A simplified priority queue implementation. Is stable, auto-resizes, and thread-safe, at the cost of being slightly slower than /// FastPriorityQueue /// This class is kept here for backwards compatibility. It's recommended you use SimplePriorityQueue<TItem, TPriority> /// /// The type to enqueue public class SimplePriorityQueue : SimplePriorityQueue { /// /// Instantiate a new Priority Queue /// public SimplePriorityQueue() { } /// /// Instantiate a new Priority Queue /// /// The comparer used to compare priority values. Defaults to Comparer<float>.default public SimplePriorityQueue(IComparer comparer) : base(comparer) { } /// /// Instantiate a new Priority Queue /// /// The comparison function to use to compare priority values public SimplePriorityQueue(Comparison comparer) : base(comparer) { } } }