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) { }
}
}