You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

最小堆理解、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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 08:11:22