using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Diagnostics; namespace CarFire { /// /// Why .NET doesn't already provide such a basic data structure /// as a priority queue is anybody's best guess. This is an interface /// for a queue class which treats the items themselves as keys, /// the relative priorities being determined by a comparison between /// them. /// /// The type of item to queue. This must be a /// comparable class. public interface IPriorityQueue where T : IComparable { /// /// Add a new item to the priority queue. /// /// The item to add. void Add(T item); /// /// Get the item with the highest priority, removing it /// from the priority queue. /// /// The highest priority item. T GetNext(); /// /// Get the item with the highest priority, but keep it in /// the priority queue. /// /// The highest priority item. T Peek(); /// /// Notify the priority queue that the priority of a certain item /// has improved. If the items is not in the queue, it is added. /// Do not use this to demote items. The internal structures will /// be reconfigured in order to ensure correct priority queueing. /// /// The item that improved. void Promote(T item); /// /// Remove all the items from the priority queue. /// void Clear(); /// /// Get the number of items in the priority queue. /// int Count { get; } } /// /// A minimum binary heap implementing a priority queue. /// /// The type of items to load in the heap. public class BinaryHeap : IPriorityQueue where T : IComparable { #region Public Properties /// /// Get the number of items in the binary heap. /// public int Count { get { return mSize; } } #endregion #region Public Methods /// /// Construct a binary heap. /// public BinaryHeap() { mList.Add(default(T)); } /// /// Construct a binary heap with a predetermined capacity. /// The heap is dynamically-sized, but it will be more /// efficient if you set the capacity to the number of items /// you plan to add. /// /// The capacity of the heap. public BinaryHeap(int capacity) { mList.Capacity = capacity; mList.Add(default(T)); } /// /// Add a new item to the heap. /// /// The item to add. public void Add(T item) { mList.Add(item); mSize++; PercolateUp(mSize); } /// /// Get the item with the lowest value, removing it from the heap. /// /// The highest priority item. public T GetNext() { Debug.Assert(mSize > 0); T next = mList[1]; mList[1] = mList[mSize]; PercolateDown(1); mList.RemoveAt(mSize); mSize--; return next; } /// /// Get the item with the lowest value, but keep it in the heap. /// /// The highest priority item. public T Peek() { Debug.Assert(mSize > 0); return mList[1]; } /// /// Decrease the value of one of the items in the heap. If the /// item is not already in the heap, it is added. /// /// The item to change. public void Promote(T item) { for (int i = 1; i < mList.Count; i++) { if (item.Equals(mList[i])) { mList[i] = item; if (PercolateUp(i) == i) PercolateDown(i); return; } } Add(item); } /// /// Remove all the items from the heap. /// public void Clear() { mList.Clear(); mSize = 0; } #endregion #region Private Methods int PercolateUp(int index) { T temp = mList[index]; for (int parent; index > 1; index = parent) { parent = index / 2; if (temp.CompareTo(mList[parent]) < 0) mList[index] = mList[parent]; else break; } mList[index] = temp; return index; } int PercolateDown(int index) { T temp = mList[index]; for (int child; index * 2 <= mSize; index = child) { child = index * 2; if (child != mSize && mList[child].CompareTo(mList[child + 1]) > 0) child++; if (temp.CompareTo(mList[child]) > 0) mList[index] = mList[child]; else break; } mList[index] = temp; return index; } #endregion #region Private Variables List mList = new List(); int mSize = 0; #endregion } /// /// The binary heap might be too slow for shortest path algorithms, /// so here is a fibonacci heap which should perform better. /// /// The type of items to load in the heap. public class FibonacciHeap : IPriorityQueue where T : IComparable { #region Public Properties /// /// Get the number of items in the fibonacci heap. /// public int Count { get { return mCount; } } #endregion #region Public Methods /// /// Construct an empty fibonacci heap. /// public FibonacciHeap() { mList = new LinkedList(); mLookup = new Dictionary>(); } /// /// Add a new item to the heap. /// /// The item to add. public void Add(T item) { LinkedListNode node = mList.AddLast(new NodeInfo(item)); if (mMin == null || item.CompareTo(mMin.Value.Item) < 0) mMin = node; mLookup[item] = node; mCount++; } /// /// Get the item with the lowest value, removing it from the heap. /// /// The highest priority item. public T GetNext() { LinkedListNode temp = mMin; mList.Remove(mMin); mMin = null; LinkedListNode node = temp.Value.Children.First; while (node != null) { Cut(node); node = temp.Value.Children.First; } Consolidate(); mLookup.Remove(temp.Value.Item); mCount--; return temp.Value.Item; } /// /// Get the item with the lowest value, but keep it in the heap. /// /// The highest priority item. public T Peek() { return mMin.Value.Item; } /// /// Decrease the value of one of the items in the heap. If the /// item is not already in the heap, it is added. /// /// The item to change. public void Promote(T item) { LinkedListNode node; if (mLookup.TryGetValue(item, out node)) { LinkedListNode parent = node.Value.Parent; if (parent != null) { if (node.Value.Item.CompareTo(parent.Value.Item) < 0) { Cut(node); CascadingCut(parent); if (node.Value.Item.CompareTo(mMin.Value.Item) < 0) mMin = node; } } else if (node.Value.Item.CompareTo(mMin.Value.Item) < 0) mMin = node; } else { Add(item); } } /// /// Remove all the items from the heap. /// public void Clear() { mList.Clear(); mMin = null; mCount = 0; mLookup.Clear(); } #endregion #region Private Methods // Builds up the binomial trees. More importantly, it sets the min // pointer and ensures that no root binomial tree has the same degree. void Consolidate() { LinkedListNode[] degree = new LinkedListNode[64]; for (LinkedListNode node = mList.First; node != null;) { LinkedListNode match = degree[node.Value.Degree]; if (match == null) { if (mMin == null || node.Value.Item.CompareTo(mMin.Value.Item) <= 0) mMin = node; degree[node.Value.Degree] = node; node = node.Next; } else { degree[node.Value.Degree] = null; if (node.Value.Item.CompareTo(match.Value.Item) < 0) { Link(match, node); } else { mList.Remove(match); mList.AddAfter(node, match); Link(node, match); node = match; } } } } // Removes child from whatever list it is in and makes it a child // of parent. This is for building up the binomial trees. void Link(LinkedListNode child, LinkedListNode parent) { child.List.Remove(child); parent.Value.Children.AddLast(child); child.Value.Parent = parent; child.Value.IsMarked = false; } // Removes node from whatever list it is in and makes it a root binomial // tree. void Cut(LinkedListNode node) { node.List.Remove(node); mList.AddLast(node); node.Value.Parent = null; node.Value.IsMarked = false; } // Cuts each node that is marked, following the ancestry until it finds // an unmarked ancestor (in which case it marks it) or a root node. void CascadingCut(LinkedListNode node) { while (node.Value.Parent != null) { if (node.Value.IsMarked) { Cut(node); node = node.Value.Parent; } else { node.Value.IsMarked = true; break; } } } #endregion #region Private Types // This container class has the information we need to form // a tree out of linked lists. class NodeInfo { public LinkedListNode Parent; public LinkedList Children = new LinkedList(); public bool IsMarked = false; public int Degree { get { return Children.Count; } } public T Item { get { return mItem; } } public NodeInfo(T item) { mItem = item; } T mItem; } #endregion #region Private Variables LinkedList mList; LinkedListNode mMin; int mCount; // To speed up promotions, we need a fast lookup for nodes. Dictionary> mLookup; #endregion } }