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