e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
238 lines
7.3 KiB
C#
238 lines
7.3 KiB
C#
// <copyright file="KeyedPriorityQueue.cs" company="Microsoft">Copyright (c) Microsoft Corporation. All rights reserved.</copyright>
|
|
|
|
using System;
|
|
using System.Diagnostics;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
|
|
namespace System.Workflow.Runtime
|
|
{
|
|
internal sealed class KeyedPriorityQueueHeadChangedEventArgs<T> : EventArgs where T : class
|
|
{
|
|
private T oldFirstElement;
|
|
private T newFirstElement;
|
|
|
|
public KeyedPriorityQueueHeadChangedEventArgs(T oldFirstElement, T newFirstElement)
|
|
{
|
|
this.oldFirstElement = oldFirstElement;
|
|
this.newFirstElement = newFirstElement;
|
|
}
|
|
|
|
public T OldFirstElement { get { return oldFirstElement; } }
|
|
public T NewFirstElement { get { return newFirstElement; } }
|
|
}
|
|
|
|
/// <summary> Combines the functionality of a dictionary and a heap-sorted priority queue.
|
|
/// Enqueue and Dequeue operations are O(log n), Peek is O(1) and Remove is O(n).
|
|
/// Used by the SchedulerService classes to maintain an ordered list of running timers, etc.
|
|
/// Lesser priority values are higher priority.</summary>
|
|
/// <typeparam name="K">Key</typeparam>
|
|
/// <typeparam name="V">Value</typeparam>
|
|
/// <typeparam name="P">Priority</typeparam>
|
|
[Serializable]
|
|
internal class KeyedPriorityQueue<K, V, P> where V : class
|
|
{
|
|
private List<HeapNode<K, V, P>> heap = new List<HeapNode<K, V, P>>();
|
|
private int size;
|
|
private Comparer<P> priorityComparer = Comparer<P>.Default;
|
|
private HeapNode<K, V, P> placeHolder = default(HeapNode<K, V, P>);
|
|
|
|
public event EventHandler<KeyedPriorityQueueHeadChangedEventArgs<V>> FirstElementChanged;
|
|
|
|
public KeyedPriorityQueue()
|
|
{
|
|
heap.Add(default(HeapNode<K, V, P>)); // Dummy zeroth element, heap is 1-based
|
|
}
|
|
|
|
public void Enqueue(K key, V value, P priority)
|
|
{
|
|
V oldHead = size > 0 ? heap[1].Value : null;
|
|
int i = ++size;
|
|
int parent = i / 2;
|
|
if (i == heap.Count)
|
|
heap.Add(placeHolder);
|
|
while (i > 1 && IsHigher(priority, heap[parent].Priority))
|
|
{
|
|
heap[i] = heap[parent];
|
|
i = parent;
|
|
parent = i / 2;
|
|
}
|
|
heap[i] = new HeapNode<K, V, P>(key, value, priority);
|
|
V newHead = heap[1].Value;
|
|
if (!newHead.Equals(oldHead))
|
|
{
|
|
RaiseHeadChangedEvent(oldHead, newHead);
|
|
}
|
|
}
|
|
|
|
public V Dequeue()
|
|
{
|
|
V oldHead = (size < 1) ? null : DequeueImpl();
|
|
V newHead = (size < 1) ? null : heap[1].Value;
|
|
RaiseHeadChangedEvent(null, newHead);
|
|
return oldHead;
|
|
}
|
|
|
|
private V DequeueImpl()
|
|
{
|
|
Debug.Assert(size > 0, "Queue Underflow");
|
|
V oldHead = heap[1].Value;
|
|
heap[1] = heap[size];
|
|
heap[size--] = placeHolder;
|
|
Heapify(1);
|
|
return oldHead;
|
|
}
|
|
|
|
|
|
public V Remove(K key)
|
|
{
|
|
if (size < 1)
|
|
return null;
|
|
|
|
V oldHead = heap[1].Value;
|
|
for (int i = 1; i <= size; i++)
|
|
{
|
|
if (heap[i].Key.Equals(key))
|
|
{
|
|
V retval = heap[i].Value;
|
|
Swap(i, size);
|
|
heap[size--] = placeHolder;
|
|
Heapify(i);
|
|
V newHead = heap[1].Value;
|
|
if (!oldHead.Equals(newHead))
|
|
{
|
|
RaiseHeadChangedEvent(oldHead, newHead);
|
|
}
|
|
return retval;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public V Peek()
|
|
{
|
|
return (size < 1) ? null : heap[1].Value;
|
|
}
|
|
|
|
public int Count
|
|
{
|
|
get { return size; }
|
|
}
|
|
|
|
public V FindByPriority(P priority, Predicate<V> match)
|
|
{
|
|
return size < 1 ? null : Search(priority, 1, match);
|
|
}
|
|
|
|
public ReadOnlyCollection<V> Values
|
|
{
|
|
get
|
|
{
|
|
List<V> values = new List<V>();
|
|
for (int i = 1; i <= size; i++)
|
|
{
|
|
values.Add(heap[i].Value);
|
|
}
|
|
return new ReadOnlyCollection<V>(values);
|
|
}
|
|
}
|
|
|
|
public ReadOnlyCollection<K> Keys
|
|
{
|
|
get
|
|
{
|
|
List<K> keys = new List<K>();
|
|
for (int i = 1; i <= size; i++)
|
|
{
|
|
keys.Add(heap[i].Key);
|
|
}
|
|
return new ReadOnlyCollection<K>(keys);
|
|
}
|
|
}
|
|
|
|
public void Clear()
|
|
{
|
|
heap.Clear();
|
|
size = 0;
|
|
}
|
|
|
|
private void RaiseHeadChangedEvent(V oldHead, V newHead)
|
|
{
|
|
if (oldHead != newHead)
|
|
{
|
|
EventHandler<KeyedPriorityQueueHeadChangedEventArgs<V>> fec = FirstElementChanged;
|
|
if (fec != null)
|
|
fec(this, new KeyedPriorityQueueHeadChangedEventArgs<V>(oldHead, newHead));
|
|
}
|
|
}
|
|
|
|
private V Search(P priority, int i, Predicate<V> match)
|
|
{
|
|
Debug.Assert(i >= 1 || i <= size, "Index out of range: i = " + i + ", size = " + size);
|
|
|
|
V value = null;
|
|
if (IsHigher(heap[i].Priority, priority))
|
|
{
|
|
if (match(heap[i].Value))
|
|
value = heap[i].Value;
|
|
int left = 2 * i;
|
|
int right = left + 1;
|
|
if (value == null && left <= size)
|
|
value = Search(priority, left, match);
|
|
if (value == null && right <= size)
|
|
value = Search(priority, right, match);
|
|
}
|
|
return value;
|
|
}
|
|
|
|
private void Heapify(int i)
|
|
{
|
|
Debug.Assert(i >= 1 || i <= size, "Index out of range: i = " + i + ", size = " + size);
|
|
|
|
int left = 2 * i;
|
|
int right = left + 1;
|
|
int highest = i;
|
|
if (left <= size && IsHigher(heap[left].Priority, heap[i].Priority))
|
|
highest = left;
|
|
if (right <= size && IsHigher(heap[right].Priority, heap[highest].Priority))
|
|
highest = right;
|
|
if (highest != i)
|
|
{
|
|
Swap(i, highest);
|
|
Heapify(highest);
|
|
}
|
|
|
|
}
|
|
|
|
private void Swap(int i, int j)
|
|
{
|
|
Debug.Assert(i >= 1 || j >= 1 || i <= size || j <= size, "Index out of range: i = " + i + ", j = " + j + ", size = " + size);
|
|
|
|
HeapNode<K, V, P> temp = heap[i];
|
|
heap[i] = heap[j];
|
|
heap[j] = temp;
|
|
}
|
|
|
|
protected virtual bool IsHigher(P p1, P p2)
|
|
{
|
|
return (priorityComparer.Compare(p1, p2) < 1);
|
|
}
|
|
|
|
[Serializable]
|
|
private struct HeapNode<KK, VV, PP>
|
|
{
|
|
public KK Key;
|
|
public VV Value;
|
|
public PP Priority;
|
|
|
|
public HeapNode(KK key, VV value, PP priority)
|
|
{
|
|
Key = key;
|
|
Value = value;
|
|
Priority = priority;
|
|
}
|
|
}
|
|
}
|
|
}
|