blob: ecc4759b7dab8e1dae66ed8b721eb744cd981ecb [file] [log] [blame]
tome3489412014-08-29 02:30:38 -07001package org.onlab.graph;
2
3import com.google.common.collect.ImmutableList;
4
5import java.util.Comparator;
6import java.util.Iterator;
7import java.util.List;
8import java.util.Objects;
9
tomeadbb462014-09-07 16:10:19 -070010import static com.google.common.base.MoreObjects.toStringHelper;
tome3489412014-08-29 02:30:38 -070011import static com.google.common.base.Preconditions.checkNotNull;
12
13/**
14 * Implementation of an array-backed heap structure whose sense of order is
15 * imposed by the provided comparator.
16 * <p/>
17 * While this provides similar functionality to {@link java.util.PriorityQueue}
18 * data structure, one key difference is that external entities can control
19 * when to restore the heap property, which is done through invocation of the
20 * {@link #heapify} method.
21 * <p/>
22 * This class is not thread-safe and care must be taken to prevent concurrent
23 * modifications.
24 *
25 * @param <T> type of the items on the heap
26 */
27public class Heap<T> {
28
tome3489412014-08-29 02:30:38 -070029 private final List<T> data;
30 private final Comparator<T> comparator;
31
32 /**
33 * Creates a new heap backed by the specified list. In the interest of
34 * efficiency, the list should be array-backed. Also, for the same reason,
35 * the data is not copied and therefore, the caller must assure that the
36 * backing data is not altered in any way.
37 *
38 * @param data backing data list
39 * @param comparator comparator for ordering the heap items
40 */
41 public Heap(List<T> data, Comparator<T> comparator) {
42 this.data = checkNotNull(data, "Data cannot be null");
43 this.comparator = checkNotNull(comparator, "Comparator cannot be null");
44 heapify();
45 }
46
47 /**
48 * Restores the heap property by re-arranging the elements in the backing
49 * array as necessary following any heap modifications.
50 */
51 public void heapify() {
52 for (int i = data.size() / 2; i >= 0; i--) {
53 heapify(i);
54 }
55 }
56
57 /**
58 * Returns the current size of the heap.
59 *
60 * @return number of items in the heap
61 */
62 public int size() {
63 return data.size();
64 }
65
66 /**
67 * Returns true if there are no items in the heap.
68 *
69 * @return true if heap is empty
70 */
71 public boolean isEmpty() {
72 return data.isEmpty();
73 }
74
75 /**
76 * Returns the most extreme item in the heap.
77 *
78 * @return heap extreme or null if the heap is empty
79 */
80 public T extreme() {
81 return data.isEmpty() ? null : data.get(0);
82 }
83
84 /**
85 * Extracts and returns the most extreme item from the heap.
86 *
87 * @return heap extreme or null if the heap is empty
88 */
89 public T extractExtreme() {
90 if (!isEmpty()) {
91 T extreme = extreme();
92
93 data.set(0, data.get(data.size() - 1));
94 data.remove(data.size() - 1);
95 heapify();
96 return extreme;
97 }
98 return null;
99 }
100
101 /**
102 * Inserts the specified item into the heap and returns the modified heap.
103 *
104 * @param item item to be inserted
105 * @return the heap self
106 * @throws IllegalArgumentException if the heap is already full
107 */
108 public Heap<T> insert(T item) {
109 data.add(item);
110 bubbleUp();
111 return this;
112 }
113
114 /**
115 * Returns iterator to traverse the heap level-by-level. This iterator
116 * does not permit removal of items.
117 *
118 * @return non-destructive heap iterator
119 */
120 public Iterator<T> iterator() {
121 return ImmutableList.copyOf(data).iterator();
122 }
123
124 // Bubbles up the last item in the heap to its proper position to restore
125 // the heap property.
126 private void bubbleUp() {
127 int child = data.size() - 1;
128 while (child > 0) {
129 int parent = child / 2;
130 if (comparator.compare(data.get(child), data.get(parent)) < 0) {
131 break;
132 }
133 swap(child, parent);
134 child = parent;
135 }
136 }
137
138 // Restores the heap property of the specified heap layer.
139 private void heapify(int i) {
140 int left = 2 * i + 1;
141 int right = 2 * i;
142 int extreme = i;
143
144 if (left < data.size() &&
145 comparator.compare(data.get(extreme), data.get(left)) < 0) {
146 extreme = left;
147 }
148
149 if (right < data.size() &&
150 comparator.compare(data.get(extreme), data.get(right)) < 0) {
151 extreme = right;
152 }
153
154 if (extreme != i) {
155 swap(i, extreme);
156 heapify(extreme);
157 }
158 }
159
160 // Swaps two heap items identified by their respective indexes.
161 private void swap(int i, int k) {
162 T aux = data.get(i);
163 data.set(i, data.get(k));
164 data.set(k, aux);
165 }
166
167 @Override
168 public boolean equals(Object obj) {
169 if (obj instanceof Heap) {
170 Heap that = (Heap) obj;
171 return this.getClass() == that.getClass() &&
172 Objects.equals(this.comparator, that.comparator) &&
173 Objects.deepEquals(this.data, that.data);
174 }
175 return false;
176 }
177
178 @Override
179 public int hashCode() {
180 return Objects.hash(comparator, data);
181 }
182
183 @Override
184 public String toString() {
tomeadbb462014-09-07 16:10:19 -0700185 return toStringHelper(this)
tome3489412014-08-29 02:30:38 -0700186 .add("data", data)
187 .add("comparator", comparator)
188 .toString();
189 }
190
191}