blob: e6d764456edd190e9cdef1c5a377834588d8500b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.onlab.graph;
import com.google.common.collect.ImmutableList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Implementation of an array-backed heap structure whose sense of order is
* imposed by the provided comparator.
* <p/>
* While this provides similar functionality to {@link java.util.PriorityQueue}
* data structure, one key difference is that external entities can control
* when to restore the heap property, which is done through invocation of the
* {@link #heapify} method.
* <p/>
* This class is not thread-safe and care must be taken to prevent concurrent
* modifications.
*
* @param <T> type of the items on the heap
*/
public class Heap<T> {
private final List<T> data;
private final Comparator<T> comparator;
/**
* Creates a new heap backed by the specified list. In the interest of
* efficiency, the list should be array-backed. Also, for the same reason,
* the data is not copied and therefore, the caller must assure that the
* backing data is not altered in any way.
*
* @param data backing data list
* @param comparator comparator for ordering the heap items
*/
public Heap(List<T> data, Comparator<T> comparator) {
this.data = checkNotNull(data, "Data cannot be null");
this.comparator = checkNotNull(comparator, "Comparator cannot be null");
heapify();
}
/**
* Restores the heap property by re-arranging the elements in the backing
* array as necessary following any heap modifications.
*/
public void heapify() {
for (int i = data.size() / 2; i >= 0; i--) {
heapify(i);
}
}
/**
* Returns the current size of the heap.
*
* @return number of items in the heap
*/
public int size() {
return data.size();
}
/**
* Returns true if there are no items in the heap.
*
* @return true if heap is empty
*/
public boolean isEmpty() {
return data.isEmpty();
}
/**
* Returns the most extreme item in the heap.
*
* @return heap extreme or null if the heap is empty
*/
public T extreme() {
return data.isEmpty() ? null : data.get(0);
}
/**
* Extracts and returns the most extreme item from the heap.
*
* @return heap extreme or null if the heap is empty
*/
public T extractExtreme() {
if (!isEmpty()) {
T extreme = extreme();
data.set(0, data.get(data.size() - 1));
data.remove(data.size() - 1);
heapify();
return extreme;
}
return null;
}
/**
* Inserts the specified item into the heap and returns the modified heap.
*
* @param item item to be inserted
* @return the heap self
* @throws IllegalArgumentException if the heap is already full
*/
public Heap<T> insert(T item) {
data.add(item);
bubbleUp();
return this;
}
/**
* Returns iterator to traverse the heap level-by-level. This iterator
* does not permit removal of items.
*
* @return non-destructive heap iterator
*/
public Iterator<T> iterator() {
return ImmutableList.copyOf(data).iterator();
}
// Bubbles up the last item in the heap to its proper position to restore
// the heap property.
private void bubbleUp() {
int child = data.size() - 1;
while (child > 0) {
int parent = child / 2;
if (comparator.compare(data.get(child), data.get(parent)) < 0) {
break;
}
swap(child, parent);
child = parent;
}
}
// Restores the heap property of the specified heap layer.
private void heapify(int i) {
int left = 2 * i + 1;
int right = 2 * i;
int extreme = i;
if (left < data.size() &&
comparator.compare(data.get(extreme), data.get(left)) < 0) {
extreme = left;
}
if (right < data.size() &&
comparator.compare(data.get(extreme), data.get(right)) < 0) {
extreme = right;
}
if (extreme != i) {
swap(i, extreme);
heapify(extreme);
}
}
// Swaps two heap items identified by their respective indexes.
private void swap(int i, int k) {
T aux = data.get(i);
data.set(i, data.get(k));
data.set(k, aux);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj instanceof Heap) {
Heap that = (Heap) obj;
return this.getClass() == that.getClass() &&
Objects.equals(this.comparator, that.comparator) &&
Objects.deepEquals(this.data, that.data);
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(comparator, data);
}
@Override
public String toString() {
return toStringHelper(this)
.add("data", data)
.add("comparator", comparator)
.toString();
}
}