堆属性是指在完全二叉树结构中,每个节点与其直接子节点必须满足的大小关系:最大堆要求父节点值≥左右子节点值,最小堆则要求父节点值≤左右子节点值;该性质对所有非叶子节点成立,且不约束兄弟节点间关系。

什么是堆属性,为什么不能只比相邻元素
堆属性不是指数组升序或降序,而是要求每个节点都满足与子节点的大小关系:最大堆中 arr[i] >= arr[2*i+1] 且 arr[i] >= arr[2*i+2];最小堆则反过来。常见错误是只检查 arr[i] ,这其实是检查是否有序,和堆无关。
关键点在于:堆是完全二叉树的层序存储,父节点 i 的左子为 2*i+1、右子为 2*i+2,但这些下标必须 size 才合法——越界子节点直接忽略。
用循环遍历所有非叶子节点验证
叶子节点没有子节点,无需检查;只需验证下标从 0 到 (n/2)-1 的所有非叶子节点(因为完全二叉树中,最后一个非叶子节点是 parent of last element,即 (n-2)/2 向下取整,等价于 n/2 - 1 当 n > 0)。
- 对每个
i,先检查左子:if (2*i+1 → 违反最大堆 - 再检查右子:
if (2*i+2 → 同样违反 - 只要任一比较失败,立即返回
false - 空数组或单元素数组天然满足堆属性,可直接返回
true
示例代码片段:
立即学习“C++免费学习笔记(深入)”;
bool isMaxHeap(const std::vector<int>& arr) {
int n = arr.size();
if (n <= 1) return true;
for (int i = 0; i < n/2; ++i) {
int left = 2*i + 1;
int right = 2*i + 2;
if (left < n && arr[i] < arr[left]) return false;
if (right < n && arr[i] < arr[right]) return false;
}
return true;
}std::make_heap 之后调用 is_heap 更简单
C++11 起标准库提供了 std::is_heap 和 std::is_heap_until,它们按最大堆语义检查(即默认等价于 std::less<T>),行为与手写逻辑一致,但更可靠、支持自定义比较器。
- 检查整个范围:
std::is_heap(arr.begin(), arr.end()) - 检查是否为最小堆:传入
std::greater<int>{},即std::is_heap(arr.begin(), arr.end(), std::greater<int>{}) - 注意:它不修改原数组,只是只读验证,性能与手写循环基本一致
- 如果数组含
NaN或自定义类型未正确定义operator<,可能产生意外结果
边界情况和易错点
最容易被忽略的是索引越界和比较器一致性。
- 数组为空或只有 1 个元素时,
n/2是 0,循环不执行 → 正确返回true - 当使用
std::vector::data()配合裸指针遍历时,务必确认size()准确,否则is_heap可能读越界 - 若用
std::priority_queue底层容器验证,要注意其内部存储不一定是完整堆结构(比如某些实现延迟调整),应避免直接检验其c成员 - 浮点数堆需警惕
NaN:任何与NaN的比较都为false,会导致is_heap错误返回true
真正要命的不是写错公式,而是忘了堆属性只约束父子关系,不约束兄弟之间或跨层节点——比如 [10, 5, 8, 1, 3, 7] 满足最大堆,但 5 和 8 无大小要求。


















