Paradox Game Engine  v1.0.0 beta06
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros Pages
PriorityNodeQueue.cs
Go to the documentation of this file.
1 // Copyright (c) 2014 Silicon Studio Corp. (http://siliconstudio.co.jp)
2 // This file is distributed under GPL v3. See LICENSE.md for details.
3 using System;
4 using System.Collections.Generic;
5 
6 namespace SiliconStudio.Core.Collections
7 {
8  /// <summary>
9  /// Implements a priority queue of type T.
10  ///
11  /// Elements may be added to the queue in any order, but when we pull
12  /// elements out of the queue, they will be returned in 'ascending' order.
13  /// Adding new elements into the queue may be done at any time, so this is
14  /// useful to implement a dynamically growing and shrinking queue. Both adding
15  /// an element and removing the first element are log(N) operations.
16  ///
17  /// The queue is implemented using a priority-heap data structure. For more
18  /// details on this elegant and simple data structure see "Programming Pearls"
19  /// in our library. The tree is implemented atop a list, where 2N and 2N+1 are
20  /// the child nodes of node N. The tree is balanced and left-aligned so there
21  /// are no 'holes' in this list.
22  /// </summary>
23  /// <typeparam name="T">Type T.</typeparam>
24  public class PriorityNodeQueue<T>
25  {
26  /// <summary>The List we use for implementation.</summary>
27  private List<PriorityQueueNode<T>> items = new List<PriorityQueueNode<T>>();
28 
29  // Used for comparing and sorting elements.
30  private IComparer<T> comparer;
31 
33  {
34  this.comparer = comparer;
35  }
36 
38  {
39  this.comparer = Comparer<T>.Default;
40  }
41 
42  /// <summary>Clear all the elements from the priority queue</summary>
43  public void Clear()
44  {
45  items.Clear();
46  }
47 
48  /// <summary>
49  /// Removes the specified item.
50  /// </summary>
51  /// <param name="item">The item to remove.</param>
52  public void Remove(PriorityQueueNode<T> item)
53  {
54  int index = item.Index;
55  if (index == -1)
56  return;
57 
58  // The element to return is of course the first element in the array,
59  // or the root of the tree. However, this will leave a 'hole' there. We
60  // fill up this hole with the last element from the array. This will
61  // break the heap property. So we bubble the element downwards by swapping
62  // it with it's lower child until it reaches it's correct level. The lower
63  // child (one of the orignal elements with index 1 or 2) will now be at the
64  // head of the queue (root of the tree).
65  int nMax = items.Count - 1;
66  var itemMax = items[nMax];
67  itemMax.Index = index;
68  var itemToRemove = items[index];
69  itemToRemove.Index = -1;
70  items[index] = itemMax;
71  items.RemoveAt(nMax); // Move the last element to the top
72 
73  int p = index;
74  while (true)
75  {
76  // c is the child we want to swap with. If there
77  // is no child at all, then the heap is balanced
78  int c = p * 2; if (c >= nMax) break;
79 
80  // If the second child is smaller than the first, that's the one
81  // we want to swap with this parent.
82  if (c + 1 < nMax && comparer.Compare(items[c + 1].Value, items[c].Value) < 0) c++;
83  // If the parent is already smaller than this smaller child, then
84  // we are done
85  if (comparer.Compare(items[p].Value, items[c].Value) <= 0)
86  break;
87 
88  // Othewise, swap parent and child, and follow down the parent
89  var tmp = items[p];
90  var tmp2 = items[c];
91  tmp.Index = c;
92  tmp2.Index = p;
93  items[p] = tmp2;
94  items[c] = tmp;
95  p = c;
96  }
97 
98  item.Index = -1;
99  }
100 
101  /// <summary>Add an element to the priority queue - O(log(n)) time operation.</summary>
102  /// <param name="item">The item to be added to the queue</param>
103  /// <returns>A node representing the item.</returns>
105  {
106  var result = new PriorityQueueNode<T>(item);
107  Enqueue(result);
108  return result;
109  }
110 
111  /// <summary>Add an element to the priority queue - O(log(n)) time operation.</summary>
112  /// <param name="item">The item to be added to the queue</param>
113  public void Enqueue(PriorityQueueNode<T> item)
114  {
115  if (item.Index != -1)
116  throw new InvalidOperationException("Item belongs to another PriorityNodeQueue.");
117 
118  // We add the item to the end of the list (at the bottom of the
119  // tree). Then, the heap-property could be violated between this element
120  // and it's parent. If this is the case, we swap this element with the
121  // parent (a safe operation to do since the element is known to be less
122  // than it's parent). Now the element move one level up the tree. We repeat
123  // this test with the element and it's new parent. The element, if lesser
124  // than everybody else in the tree will eventually bubble all the way up
125  // to the root of the tree (or the head of the list). It is easy to see
126  // this will take log(N) time, since we are working with a balanced binary
127  // tree.
128  int n = items.Count;
129  items.Add(item);
130  item.Index = n;
131  while (n != 0)
132  {
133  int p = n / 2; // This is the 'parent' of this item
134  if (comparer.Compare(items[n].Value, items[p].Value) >= 0)
135  break; // Item >= parent
136 
137  // Swap item and parent
138  var tmp = items[n];
139  var tmp2 = items[p];
140  tmp2.Index = n;
141  tmp.Index = p;
142  items[n] = tmp2;
143  items[p] = tmp;
144 
145  n = p; // And continue
146  }
147  }
148 
149  /// <summary>Returns the number of elements in the queue.</summary>
150  public int Count
151  {
152  get { return items.Count; }
153  }
154 
155  /// <summary>Returns true if the queue is empty.</summary>
156  /// Trying to call Peek() or Next() on an empty queue will throw an exception.
157  /// Check using Empty first before calling these methods.
158  public bool Empty
159  {
160  get { return items.Count == 0; }
161  }
162 
163  /// <summary>Allows you to look at the first element waiting in the queue, without removing it.</summary>
164  /// This element will be the one that will be returned if you subsequently call Next().
165  public T Peek()
166  {
167  return items[0].Value;
168  }
169 
170  /// <summary>Removes and returns the first element from the queue (least element)</summary>
171  /// <returns>The first element in the queue, in ascending order.</returns>
172  public T Dequeue()
173  {
174  // The element to return is of course the first element in the array,
175  // or the root of the tree. However, this will leave a 'hole' there. We
176  // fill up this hole with the last element from the array. This will
177  // break the heap property. So we bubble the element downwards by swapping
178  // it with it's lower child until it reaches it's correct level. The lower
179  // child (one of the orignal elements with index 1 or 2) will now be at the
180  // head of the queue (root of the tree).
181  int nMax = items.Count - 1;
182  var itemToRemove = items[0];
183  var itemMax = items[nMax];
184  itemMax.Index = 0;
185  var val = itemToRemove.Value;
186  itemToRemove.Index = -1;
187  items[0] = itemMax;
188  items.RemoveAt(nMax); // Move the last element to the top
189 
190  int p = 0;
191  while (true)
192  {
193  // c is the child we want to swap with. If there
194  // is no child at all, then the heap is balanced
195  int c = p * 2; if (c >= nMax) break;
196 
197  // If the second child is smaller than the first, that's the one
198  // we want to swap with this parent.
199  if (c + 1 < nMax && comparer.Compare(items[c + 1].Value, items[c].Value) < 0) c++;
200  // If the parent is already smaller than this smaller child, then
201  // we are done
202  if (comparer.Compare(items[p].Value, items[c].Value) <= 0)
203  break;
204 
205  // Othewise, swap parent and child, and follow down the parent
206  var tmp = items[p];
207  var tmp2 = items[c];
208  tmp.Index = c;
209  tmp2.Index = p;
210  items[p] = tmp2;
211  items[c] = tmp;
212 
213  p = c;
214  }
215 
216  return val;
217  }
218  }
219 }
T Dequeue()
Removes and returns the first element from the queue (least element)
void Enqueue(PriorityQueueNode< T > item)
Add an element to the priority queue - O(log(n)) time operation.
void Clear()
Clear all the elements from the priority queue
Represents a node in a priority queue, to allow O(n) removal.
void Remove(PriorityQueueNode< T > item)
Removes the specified item.
T Peek()
Allows you to look at the first element waiting in the queue, without removing it.
PriorityQueueNode< T > Enqueue(T item)
Add an element to the priority queue - O(log(n)) time operation.