堆是一种基于完全二叉树的数据结构,其中每个父节点都大于等于/小于等于其子节点。可以分为最大堆(Max Heap)和最小堆(Min Heap),其中最大堆要求父节点的值大于或等于所有子节点,而最小堆要求父节点的值小于或等于所有子节点。
下面是一个以最大堆为例的java代码简单实现:
- import java.util.Arrays;
- import java.util.NoSuchElementException;
-
- class Heap {
- private int[] heapArray;
- private int maxSize;
- private int currentSize;
-
- public Heap(int size) {// 构造函数,创建一个具有指定大小的堆对象。
- this.maxSize = size;
- this.currentSize = 0;
- this.heapArray = new int[size];
- }
-
- public boolean isEmpty() {// 检查堆是否为空,并返回布尔值。
- return currentSize == 0;
- }
-
- public boolean isFull() {// 检查堆是否已满,并返回布尔值。
- return currentSize == maxSize;
- }
-
- public void insert(int value) {// 向堆中插入一个元素。如果堆已满,则抛出异常。该方法使用“上溯”操作来维护堆性质。
- if (isFull()) {
- throw new IllegalStateException("Heap is full");
- }
-
- heapArray[currentSize] = value;
-
- trickleUp(currentSize);
-
- currentSize++;
- }
-
- private void trickleUp(int index) {//从给定索引开始执行上溯操作以维护堆性质。它将当前节点与其父节点进行比较和交换,直到达到合适的位置或达到根节点为止。
- int parentIndex = (index - 1) / 2; // 计算父节点索引
-
- while (index > 0 && heapArray[parentIndex] < heapArray[index]) {
- // 交换父节点与子节点的值
- int temp = heapArray[parentIndex];
- heapArray[parentIndex] = heapArray[index];
- heapArray[index] = temp;
-
- index = parentIndex;
- parentIndex = (index - 1) / 2; // 更新父节点索引
- }
- }
-
- public int remove() {// 移除并返回顶部(最大)元素。如果堆为空,则抛出异常。该方法使用“下溯”操作来维护堆性质。
- if (isEmpty()) {
- throw new IllegalStateException("Heap is empty");
- }
-
- int root = heapArray[0]; // 根节点的值为最大/最小元素
-
- currentSize--;
-
- heapArray[0] = heapArray[currentSize];
-
- trickleDown(0);
-
- return root;
- }
-
- private void trickleDown(int index) {// 从给定索引开始执行下溯操作以维护堆性质。它将当前节点与其较大子节点进行比较和交换,直到没有子节点或找到合适的位置为止。
- int largerChildIndex;
-
- while (index < currentSize / 2) { // 判断是否有子节点
- int leftChildIndex = 2 * index + 1;
- int rightChildIndex= leftChildIndex + 1;
-
- if (rightChildIndex < currentSize &&
- heapArray[leftChildIndex]
- largerChildIndex=rightChildIndex;
- } else{
- largerChildIndex=leftChildIndex ;
- }
-
- if(heapArray[index]>=heapArray[largerChildIndex]){
- break; // 当前位置已满足堆性质,不需要调整,退出循环
- }
-
- swap(index, largerChildIndex);
- index=largerChildIndex;//更新索引以继续下一轮比较
- }
- }
-
-
- private void swap(int index1,int index2){ //用于交换数组中两个索引位置上的元素。
- int temp=heapArray[index1];
- heapArray[index1]=heapArray[index2];
- heapArray[index2] = temp;
- }
-
- public int[] getHeapArray() {// 返回堆中所有元素
- return Arrays.copyOf(heapArray, currentSize);
- }
-
- public int peek() {// 查看但不删除顶部元素
- if (currentSize == 0) {
- throw new NoSuchElementException("Heap is empty.");
- }
-
- return heapArray[0];
- }
- }
-
- public class Main {
- public static void main(String[] args) {
- //创建一个最大堆
- Heap heap = new Heap(10);
-
- // 插入元素
- heap.insert(80);
- heap.insert(75);
- heap.insert(60);
- heap.insert(68);
- heap.insert(55);
-
- // 打印堆中所有元素
- System.out.println("Current elements in the heap: " + Arrays.toString(heap.getHeapArray()));
-
- // 获取并移除顶部(最大)元素
- int maxElement=heap.remove();
- System.out.println("Maximum element removed from the heap: "+maxElement);
-
-
- //打印删除顶部元 素后的堆
- System.out.println("Current elements in the heap after removal:" + Arrays.toString(heap.getHeapArray()) );
-
- //插入新元素
- heap.insert(100);
-
- //查看但不删除顶部元素
- maxElement=heap.peek();
- System.out.println("Maximum element removed from the heap: "+maxElement);
-
- //打印堆中所有元素
- System.out.println("Current elements in the heap: " + Arrays.toString(heap.getHeapArray()));
- }
- }
常见问题:
- 元素大小问题:在构建或插入时,需要确保父节点与子节点具有正确的大小关系。如果使用自定义对象作为堆中的元素,请实现正确的比较器来定义大小关系。
- 动态调整容量:如果底层数组满了,则需要进行动态扩展以容纳更多的元素。这可能涉及重新分配内存和复制数据,并带来一些性能开销。
- 并发问题:Java提供了线程安全的优先队列实现,如
PriorityBlockingQueue
。如果在多线程环境中使用,请确保选择适当的实现以避免并发问题。
总结:
堆是一种基于完全二叉树的数据结构,通过父节点与子节点之间的比较来维护其特性。它支持快速插入、删除和获取最大/最小元素的操作,并用于优先队列和排序算法(如堆排序)等场景。处理大小关系、动态调整容量和并发问题时要注意正确性和性能。