1. 堆与堆排序基础解析
堆是一种特殊的完全二叉树结构,在算法和数据结构领域有着广泛的应用。理解堆的核心在于掌握其两种基本形态:大顶堆和小顶堆。
大顶堆的性质是任意节点的值都大于或等于其子节点的值,而小顶堆则相反,任意节点的值都小于或等于其子节点的值。这种特性使得堆能够高效地维护数据的极值,这也是堆排序算法的基础。
在实际编程实现中,我们通常使用数组来表示堆结构。这种存储方式既节省空间又便于操作。对于数组中索引为i的节点:
- 其左子节点索引为2i+1
- 右子节点索引为2i+2
- 父节点索引为⌊(i-1)/2⌋
这种数组表示法的优势在于:
- 完全避免了指针存储,节省内存空间
- 通过简单的算术运算就能快速定位父子节点
- 适合现代CPU的缓存机制,访问效率高
注意:在构建堆时,我们只需要从最后一个非叶子节点(即索引为n/2-1的节点)开始调整即可,因为叶子节点本身已经满足堆的性质。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 堆排序的完整实现
堆排序算法可以分为两个主要阶段:建堆阶段和排序阶段。下面我们详细分析每个步骤的实现细节。
2.1 建堆过程详解
建堆的核心操作是"堆化"(heapify),即调整某个节点使其满足堆的性质。这是一个自上而下的递归过程:
java复制private void heapify(int[] nums, int n, int i) {
int largeIndex = i;
int left = 2 * i + 1;
int right = 2 * i + 2;
// 找出当前节点和左右子节点中的最大值
if (left < n && nums[left] > nums[largeIndex]) {
largeIndex = left;
}
if (right < n && nums[right] > nums[largeIndex]) {
largeIndex = right;
}
// 如果需要交换,则递归调整被交换的子树
if (largeIndex != i) {
swap(nums, i, largeIndex);
heapify(nums, n, largeIndex);
}
}
建堆时,我们从最后一个非叶子节点开始,自底向上调用heapify:
java复制public void buildMaxHeap(int[] nums) {
int n = nums.length;
for (int i = n/2-1; i >= 0; i--) {
heapify(nums, n, i);
}
}
2.2 排序过程实现
完成建堆后,排序过程相对简单:
- 将堆顶元素(最大值)与当前末尾元素交换
- 堆的大小减1(排除已排序的元素)
- 对新的堆顶元素进行heapify调整
- 重复上述步骤直到堆中只剩一个元素
java复制public void heap
