最小堆理解、heapSort实现及add/smallestChild方法校验咨询
嘿,我来帮你把堆排序的实现理清楚,顺便帮你检查下add()和smallestChild()方法的正确性~
一、HeapSort 方法的实现思路与代码示例
按照你说的算法要求(遍历数组加入堆,再取出所有元素放回数组实现升序),核心思路是用最小堆来实现:因为最小堆每次能取出当前堆中最小的元素,依次取出放回数组就能得到升序排列的结果。
假设你的堆类是基于数组实现的最小堆,Test类中的heapSort()方法可以这么写:
public class Test { public void heapSort(int[] arr) { // 初始化一个容量和数组长度一致的最小堆 MinHeap minHeap = new MinHeap(arr.length); // 第一步:遍历数组,将所有元素加入堆 for (int num : arr) { minHeap.add(num); } // 第二步:依次取出堆中最小元素,放回数组 for (int i = 0; i < arr.length; i++) { arr[i] = minHeap.extractMin(); // 这里需要堆类实现extractMin方法(取出并删除最小元素) } } }
补充说明:如果你的堆是最大堆,那要实现升序的话,需要从数组末尾开始往前填充(每次取出最大元素放到当前末尾),逻辑类似,但最小堆的方式更直接匹配你描述的“取出所有元素放回数组”的顺序。
二、add() 方法的正确性检查(最小堆场景)
最小堆的add()方法核心是先把元素放到堆的尾部,再向上调整堆结构(sift up),确保堆的性质(父节点小于子节点)。
正确的参考实现如下:
public class MinHeap { private int[] heap; private int size; // 当前堆中元素个数 private int capacity; // 堆的最大容量 public MinHeap(int capacity) { this.capacity = capacity; this.heap = new int[capacity]; this.size = 0; } // 辅助方法:获取父节点索引 private int getParentIndex(int childIndex) { return (childIndex - 1) / 2; } // 辅助方法:交换两个位置的元素 private void swap(int i, int j) { int temp = heap[i]; heap[i] = heap[j]; heap[j] = temp; } public boolean add(int value) { // 先判断堆是否已满 if (size >= capacity) { return false; // 或者实现扩容逻辑,根据你的需求调整 } // 将新元素放到堆的尾部 heap[size] = value; size++; // 向上调整堆,维护最小堆性质 siftUp(size - 1); return true; } private void siftUp(int index) { // 循环直到到达根节点,或者当前节点不小于父节点 while (index > 0) { int parentIdx = getParentIndex(index); if (heap[index] < heap[parentIdx]) { // 当前节点比父节点小,交换位置 swap(index, parentIdx); index = parentIdx; // 继续向上检查父节点 } else { // 满足最小堆性质,停止调整 break; } } } }
你可以对照自己的add()方法检查以下几点:
- 是否忘记了向上调整(siftUp)?这是最容易出错的点,如果只把元素放到尾部不调整,堆的性质就被破坏了。
- 父节点索引的计算是否正确?是不是写成了
(index + 1) / 2?正确的应该是(index - 1) / 2。 - 交换逻辑是否搞反?比如父节点比子节点小的时候还交换,这会破坏最小堆的性质。
三、smallestChild() 方法的正确性检查(最小堆场景)
smallestChild()方法一般用于**向下调整(sift down)**过程中,返回当前节点的两个子节点中较小的那个的索引,这样才能确定和哪个子节点交换来维护堆的性质。
正确的参考实现如下:
public class MinHeap { // ... 前面的成员变量和方法省略 ... // 辅助方法:获取左子节点索引 private int getLeftChildIndex(int parentIndex) { return 2 * parentIndex + 1; } // 辅助方法:获取右子节点索引 private int getRightChildIndex(int parentIndex) { return 2 * parentIndex + 2; } public int smallestChild(int parentIndex) { int leftChildIdx = getLeftChildIndex(parentIndex); int rightChildIdx = getRightChildIndex(parentIndex); // 左子节点超出范围,说明当前节点没有子节点 if (leftChildIdx >= size) { return -1; // 或者抛出异常,根据你的需求处理 } // 只有左子节点,直接返回左子节点索引 if (rightChildIdx >= size) { return leftChildIdx; } // 返回值较小的那个子节点的索引 return heap[leftChildIdx] < heap[rightChildIdx] ? leftChildIdx : rightChildIdx; } }
你可以对照自己的smallestChild()方法检查以下几点:
- 子节点索引的计算是否正确?是不是写成了
2 * parentIndex?正确的左子节点索引是2 * parentIndex + 1,右是2 * parentIndex + 2。 - 是否判断了子节点是否超出堆的实际大小?如果没判断,当节点没有子节点时会导致数组越界。
- 返回的是索引还是值?这个方法应该返回索引,方便后续交换操作,如果返回值就没法用在调整逻辑里了。
- 比较逻辑是否正确?是不是返回了较大的子节点索引?这会破坏最小堆的性质。
内容的提问来源于stack exchange,提问作者3noo6
相关产品推荐
相关产品推荐

