我正在将一个C++库移植到Java,需要使用堆(heap)数据结构。是否有标准实现可用,还是我需要自己实现?
对于Java 8,在现有的答案上进行更新:
你可以使用Java优先级队列作为堆。
最小堆:--> 保持最小元素总是在顶部,这样你就可以在O(1)的时间内访问它。
PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
最大堆:--> 为了将最大元素始终置于顶部,与上面相同的顺序。
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
这与其他回答提供的(Integer o1, Integer o2) -> Integer.compare(o2, o1)
或- Integer.compare(o1, o2)
是相同的。
您可以使用:
add
--> 将元素添加到队列中。O(log n)
remove
--> 获取并删除最小/最大值。O(log n)
peek
--> 获取最小/最大值,但不删除它。O(1)
最小堆:
PriorityQueue<Integer> minHeap = new PriorityQueue<Integer>();
最大堆:
PriorityQueue<Integer> maxHeap = new PriorityQueue<Integer>(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return - Integer.compare(o1, o2);
}
});
在Java中,PriorityQueue可以用作堆。
最小堆
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
最大堆
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
PriorityQueue使用堆来实现。根据Oracle文档的描述,https://docs.oracle.com/javase/8/docs/api/java/util/PriorityQueue.html,它似乎是二叉堆的一种实现方式。我认为目前没有斐波那契堆或成对堆的官方实现,但我很想看到其中任意一种。
从Java文档中可以得知,自1.5以来可用的PriorityQueue
类是我们应该使用的。
下面这段代码实现的是一个最小堆
,它使用默认初始化容量(11)创建了一个PriorityQueue对象,按照自然顺序(natural ordering)排序其元素,其中最小值在堆顶。
//MIN HEAP
PriorityQueue<Integer> minHeap = new PriorityQueue<>();
//equivalent to
PriorityQueue<Integer> minHeap = new PriorityQueue<>(11);
PriorityQueue(int initialCapacity, Comparator<? super E> comparator);
自1.8版本以来,我们也拥有了这个版本
PriorityQueue(Comparator<? super E> comparator);
这可以帮助您以更优雅的方式创建最大堆
,例如:
//MAX HEAP
PriorityQueue<Integer> maxHeap =
new PriorityQueue<>((n1,n2) -> Integer.compare(n2,n1));
//equivalent to
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(Comparator.reverseOrder());
针对特殊情况,请查看此示例,展示了自定义对象的自然排序,场景是根据客户到虚构餐厅的距离对客户进行排序。
import java.util.List;
import java.util.PriorityQueue;
public class DeliveryHandler {
private static final Address restaurant = new Address(5.0, 5.0);
private static class Address implements Comparable<Address> {
public double x, y;
public Address(double x, double y) {
this.x = x;
this.y = y;
}
public double distanceToShop() {
return Math.pow(restaurant.x - x, 2) + Math.pow(restaurant.y - y, 2);
}
@Override
public int compareTo(Address other) {
return Double.compare(this.distanceToShop(), other.distanceToShop());
}
@Override
public String toString() {
return "Address {x=%s, y=%s}".formatted(x, y);
}
}
public static void main(String[] args) {
List<Address> customers = List.of(
new Address(13, 14),
new Address(3, 1),
new Address(9, 20),
new Address(12, 4),
new Address(4, 4));
PriorityQueue<Address> queueServingClosest = new PriorityQueue<>();
queueServingClosest.addAll(customers);
while (!queueServingClosest.isEmpty()) {
System.out.println(queueServingClosest.remove());
}
/* Prints
Address {x=4.0, y=4.0}
Address {x=3.0, y=1.0}
Address {x=12.0, y=4.0}
Address {x=13.0, y=14.0}
Address {x=9.0, y=20.0}
*/
PriorityQueue<Address> queueServingFurthest = new PriorityQueue<>(
(a1, a2) -> Double.compare(a2.distanceToShop(), a1.distanceToShop())
);
queueServingFurthest.addAll(customers);
while (!queueServingFurthest.isEmpty()) {
System.out.println(queueServingFurthest.remove());
}
/* Prints
Address {x=9.0, y=20.0}
Address {x=13.0, y=14.0}
Address {x=12.0, y=4.0}
Address {x=3.0, y=1.0}
Address {x=4.0, y=4.0}
*/
}
}
参考资料
1- https://docs.oracle.com/javase/7/docs/api/java/util/PriorityQueue.html
2- https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/PriorityQueue.html
PriorityQueue
,guava还提供了一个MinMaxPriorityQueue
。 - Elliott Frisch