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

什么是堆属性,为什么不能只比相邻元素
堆属性不是指数组升序或降序,而是要求每个节点都满足与子节点的大小关系:最大堆中 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
示例代码片段:
bool isMaxHeap(const std::vector<int>& arr) {
int n = arr.size();
if (n <h3>std::make_heap 之后调用 is_heap 更简单</h3>
<p>C++11 起标准库提供了 <code>std::is_heap</code> 和 <code>std::is_heap_until</code>,它们按最大堆语义检查(即默认等价于 <code>std::less<t></t></code>),行为与手写逻辑一致,但更可靠、支持自定义比较器。</p>
<ul>
<li>检查整个范围:<code>std::is_heap(arr.begin(), arr.end())</code>
</li>
<li>检查是否为最小堆:传入 <code>std::greater<int>{}</int></code>,即 <code>std::is_heap(arr.begin(), arr.end(), std::greater<int>{})</int></code>
</li>
<li>注意:它不修改原数组,只是只读验证,性能与手写循环基本一致</li>
<li>如果数组含 <code>NaN</code> 或自定义类型未正确定义 <code>operator,可能产生意外结果</code>
</li>
</ul>
<h3>边界情况和易错点</h3>
<p>最容易被忽略的是索引越界和比较器一致性。</p>
<ul>
<li>数组为空或只有 1 个元素时,<code>n/2</code> 是 0,循环不执行 → 正确返回 <code>true</code>
</li>
<li>当使用 <code>std::vector::data()</code> 配合裸指针遍历时,务必确认 <code>size()</code> 准确,否则 <code>is_heap</code> 可能读越界</li>
<li>若用 <code>std::priority_queue</code> 底层容器验证,要注意其内部存储不一定是完整堆结构(比如某些实现延迟调整),应避免直接检验其 <code>c</code> 成员</li>
<li>浮点数堆需警惕 <code>NaN</code>:任何与 <code>NaN</code> 的比较都为 <code>false</code>,会导致 <code>is_heap</code> 错误返回 <code>true</code>
</li>
</ul>
<p>真正要命的不是写错公式,而是忘了堆属性只约束父子关系,不约束兄弟之间或跨层节点——比如 <code>[10, 5, 8, 1, 3, 7]</code> 满足最大堆,但 <code>5</code> 和 <code>8</code> 无大小要求。</p></int>C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











