MATSIM
BinaryMinHeap.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * BinaryMinHeap.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2013 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package org.matsim.core.router.priorityqueue;
22 
23 import java.util.ConcurrentModificationException;
24 import java.util.Iterator;
25 import java.util.NoSuchElementException;
26 
41 public class BinaryMinHeap<E extends HasIndex> implements MinHeap<E> {
42 
61  private final E[] data;
62  final double[] costs;
63  final int[] indices;
64 
65  private int heapSize;
66  private transient int modCount;
67 
83  private final boolean classicalRemove;
84 
85  private final int fanout;
86 
87  /*package*/ static final int defaultFanout = 6;
88 
89  public BinaryMinHeap(int maxSize) {
90  this(maxSize, defaultFanout, false);
91  }
92 
93  public BinaryMinHeap(int maxSize, int fanout, boolean classicalRemove) {
94  this.fanout = fanout;
95  this.classicalRemove = classicalRemove;
96 
97  this.data = (E[]) new HasIndex[maxSize];
98 
99  this.costs = new double[maxSize];
100  for (int i = 0; i < costs.length; i++) {
101  costs[i] = Double.MAX_VALUE;
102  }
103 
104  this.indices = new int[maxSize];
105  for (int i = 0; i < indices.length; i++) {
106  indices[i] = -1;
107  }
108  this.heapSize = 0;
109  this.modCount = 0;
110  }
111 
115  @Override
116  public void reset() {
117  /*
118  * For a small number of remaining entries in the heap, only removing
119  * them might be faster than overwriting all entries. However, when doing so,
120  * we have to do twice as much array accesses.
121  */
122  if (heapSize < indices.length / 10) {
123  for (int i = 0; i < heapSize; i++) {
124  indices[this.getIndex(data[i])] = -1;
125  }
126  } else {
127  for (int i = 0; i < indices.length; i++) {
128  indices[i] = -1;
129  }
130  }
131 
132  for (int i = 0; i < heapSize; i++) {
133  costs[i] = Double.MAX_VALUE;
134  }
135 
136  this.heapSize = 0;
137  this.modCount = 0;
138  }
139 
140  @Override
141  public E peek() {
142  if (isEmpty()) return null;
143  else return peek(0);
144  }
145 
146  private E peek(int index) {
147  return data[index];
148  }
149 
156  @Override
157  public E poll() {
158  E minValue;
159  if (isEmpty()) return null;
160  else {
161  this.modCount++;
162  minValue = data[0];
163  if (classicalRemove) {
164  data[0] = data[heapSize - 1];
165  costs[0] = costs[heapSize - 1];
166  indices[this.getIndex(data[0])] = 0;
167  indices[this.getIndex(minValue)] = -1;
168 
169  heapSize--;
170  if (heapSize > 0) siftDown(0);
171  } else {
172  siftDownUp(0);
173 
174  indices[this.getIndex(minValue)] = -1;
175  }
176  return minValue;
177  }
178  }
179 
180  private void siftDownUp(int index) {
181  index = removeSiftDown(index);
182 
183  // Swap entry with heap's last entry.
184  heapSize--;
185 
186  // Sift up entry that was previously at the heap's end.
187  siftUp(index, data[heapSize], costs[heapSize]);
188 
189  // Reset sentinel here:
190  costs[heapSize] = Double.MAX_VALUE;
191  }
192 
193  /*
194  * Used by alternative remove() approach. The costs have been set to
195  * Double.MAX_VALUE. Therefore we only have to compare the node's children.
196  */
197  private int removeSiftDown(int nodeIndex) {
198  while(true) {
199  int leftChildIndex = getLeftChildIndex(nodeIndex);
200  if (leftChildIndex >= heapSize) break;
201 
202  double leftCosts = costs[leftChildIndex];
203 
204  int limitChildIndex = Math.min(leftChildIndex + fanout, heapSize);
205 
206  for (int rightChildIndex = leftChildIndex + 1; rightChildIndex < limitChildIndex; rightChildIndex++) {
207  /*
208  * We use the sentinel values Double.MAX_VALUE to protect
209  * ourselves from looking beyond the heap's true size
210  */
211  double rightCosts = costs[rightChildIndex];
212  if (leftCosts >= rightCosts &&
213  (leftCosts > rightCosts || this.getIndex(data[leftChildIndex]) > this.getIndex(data[rightChildIndex]))) {
214  leftChildIndex = rightChildIndex;
215  leftCosts = rightCosts;
216  }
217  }
218 
219  copyData(nodeIndex, leftChildIndex);
220  nodeIndex = leftChildIndex;
221  }
222 
223  return nodeIndex;
224  }
225 
231  @Override
232  public int size() {
233  return this.heapSize;
234  }
235 
241  @Override
242  public boolean isEmpty() {
243  return (heapSize == 0);
244  }
245 
254  @Override
255  public boolean add(E value, double priority) {
256 
257  if (value == null) {
258  throw new NullPointerException("null values are not supported!");
259  }
260 
261  // if the element is already present in the queue, return false
262  if (indices[this.getIndex(value)] >= 0) {
263  return false;
264  } else {
265  if (heapSize == data.length) throw new RuntimeException("Heap's underlying storage is overflow!");
266 
267  this.modCount++;
268  siftUp(heapSize, value, priority);
269  heapSize++;
270  return true;
271  }
272  }
273 
281  @Override
282  public boolean remove(E value) {
283 
284  if (value == null) return false;
285 
286  /*
287  * Check the elements index. "-1" means that the element is not
288  * present in the heap.
289  */
290  int index = indices[this.getIndex(value)];
291  if (index < 0) {
292  return false;
293  } else {
294  if (classicalRemove) {
295  // Move entry to heap's top and then remove the heap's head.
296  boolean decreasedKey = decreaseKey(value, Double.NEGATIVE_INFINITY);
297  if (decreasedKey && data[0] == value) {
298  this.poll();
299  return true;
300  } else return false;
301  } else {
302  siftDownUp(index);
303 
304  // index has changed, therefore we cannot use "index" again
305  indices[this.getIndex(value)] = -1;
306  this.modCount++;
307  return true;
308  }
309  }
310  }
311 
319  @Override
320  public boolean decreaseKey(E value, double cost) {
321 
322  // If the element is not yet present in the heap, simply add it.
323  int index = indices[this.getIndex(value)];
324  if (index < 0) {
325  return this.add(value, cost);
326  }
327 
328  /*
329  * If the cost should be increased, we cannot do this. Therefore we
330  * return false.
331  */
332  double oldCost = costs[index];
333  if (oldCost < cost) return false;
334 
335  // update costs in array
336  siftUp(index, value, cost);
337  return true;
338  }
339 
348  @Override
349  public Iterator<E> iterator() {
350  return new ArrayIterator(data, heapSize);
351  }
352 
353  private void copyData(int indexTarget, int indexSource) {
354  // copy HeapEntries
355  E entry = data[indexSource];
356  data[indexTarget] = entry;
357 
358  // copy costs
359  costs[indexTarget] = costs[indexSource];
360 
361  // copy indices
362  indices[this.getIndex(entry)] = indexTarget;
363  }
364 
365  private int getLeftChildIndex(int nodeIndex) {
366  return fanout * nodeIndex + 1;
367  }
368 
369  private int getParentIndex(int nodeIndex) {
370  return (nodeIndex - 1) / fanout;
371  }
372 
373  private void siftUp(int index, E newEntry, double newCost) {
374  while (index > 0) {
375  int parentIndex = getParentIndex(index);
376  double parentCost = costs[parentIndex];
377  if (newCost > parentCost) break;
378  if (newCost == parentCost && this.getIndex(newEntry) > this.getIndex(data[parentIndex])) break;
379  this.copyData(index, parentIndex);
380 
381  // for next iteration
382  index = parentIndex;
383  }
384 
385  data[index] = newEntry;
386  costs[index] = newCost;
387  indices[this.getIndex(newEntry)] = index;
388  }
389 
390  private void siftDown(int nodeIndex) {
391 
392  int leftChildIndex = getLeftChildIndex(nodeIndex);
393  if (leftChildIndex >= heapSize) return;
394 
395  int minIndex = -1;
396  double minCosts = Double.POSITIVE_INFINITY;
397 
398  int limitChildIndex = Math.min(leftChildIndex + fanout, heapSize + 1);
399 
400  for (int childIndex = leftChildIndex; childIndex < limitChildIndex; childIndex++) {
401  /*
402  * If the costs are equal, use the array indices to define the sort order.
403  * Doing so should guarantee a deterministic order of the heap entries.
404  */
405  double childCosts = costs[childIndex];
406  if (childCosts <= minCosts) {
407  if (childCosts < minCosts || this.getIndex(data[childIndex]) < this.getIndex(data[minIndex])) {
408  minIndex = childIndex;
409  minCosts = childCosts;
410  }
411  }
412  }
413 
414  if (minIndex >= 0) {
415  swapData(nodeIndex, minIndex);
416  siftDown(minIndex);
417  }
418  }
419 
420  private void swapData(int index1, int index2) {
421 
422  // swap HeapEntries
423  E entry1 = data[index1];
424  E entry2 = data[index2];
425  data[index1] = entry2;
426  data[index2] = entry1;
427 
428  // swap costs
429  double tmpCost = costs[index1];
430  costs[index1] = costs[index2];
431  costs[index2] = tmpCost;
432 
433  // swap indices
434  indices[this.getIndex(entry1)] = index2;
435  indices[this.getIndex(entry2)] = index1;
436  }
437 
438  private int getIndex(HasIndex value) {
439  return value.getArrayIndex();
440  }
441 
442  private final class ArrayIterator implements Iterator<E> {
443 
444  private final int expectedModCount = modCount;
445 
446  private final E[] array;
447  private final int heapSize;
448  private int index = 0;
449 
450  public ArrayIterator(final E[] array, int heapSize) {
451  this.array = array;
452  this.heapSize = heapSize;
453  }
454 
455  @Override
456  public boolean hasNext() {
457  return (heapSize > index);
458  }
459 
460  @Override
461  public E next() {
462  this.checkForComodification();
463  if (!hasNext()) throw new NoSuchElementException();
464  final E value = array[index];
465  index++;
466  return value;
467  }
468 
469  @Override
470  public void remove() {
471  throw new UnsupportedOperationException("Not supported operation!");
472  }
473 
474  private void checkForComodification() {
475  if (modCount != expectedModCount) throw new ConcurrentModificationException();
476  }
477  }
478 }
void copyData(int indexTarget, int indexSource)
void siftUp(int index, E newEntry, double newCost)
BinaryMinHeap(int maxSize, int fanout, boolean classicalRemove)