C语言MAX HEAP heapify问题:仅单次堆化未处理整个链表结构
嘿,我一眼就看出你这个heapify函数的问题所在了——它只做了单次局部堆化,没处理完所有违反最大堆性质的节点链!
问题根源分析
你输入1-2-3时能得到正确结果,是因为最后一个父节点(值为1)和它的最大子节点(3)交换后,整个堆就满足性质了;但插入4后,你的代码只把最后一个父节点(值为2)和子节点4交换,得到3-4-2-1,却没继续检查4和它的父节点3的大小关系——这时候3<4,明显违反了最大堆“父节点>=子节点”的规则,但你的代码没处理这一步,所以根节点还是3而不是4。
从你给出的代码片段看,你只定位到了最后一个非叶子节点(parentcount=COUNT/2),但只对这个节点做了一次操作,既没有递归处理交换后可能再次违反堆性质的子节点,也没有向上遍历所有父节点来完成完整堆化。
修正方案:完整的堆化实现
最大堆的堆化需要从最后一个非叶子节点开始,自底向上对每个节点执行「下沉操作」——也就是如果当前节点小于它的子节点,就和最大的子节点交换,然后继续处理交换后的子节点,直到它满足堆性质。如果是插入新元素,用「上浮操作」会更高效。
1. 辅助函数:下沉操作(Sift Down)
这个函数负责把单个节点调整到符合堆性质的位置:
// 假设你的node结构包含value、parent指针,且有方法获取节点在堆中的索引(从1开始) // 另外需要实现getKthNode来获取堆中第k个节点(层序遍历的位置) void siftDown(tree *heap, node *currentNode, int COUNT) { node *largest = currentNode; node *leftChild = NULL; node *rightChild = NULL; // 获取当前节点的左右子节点(层序索引:左子=2*当前索引,右子=2*当前索引+1) int currentIdx = getNodeIndex(currentNode); leftChild = getKthNode(heap, 2 * currentIdx); rightChild = getKthNode(heap, 2 * currentIdx + 1); // 找到当前节点、左子、右子中的最大值节点 if (leftChild != NULL && leftChild->value > largest->value) { largest = leftChild; } if (rightChild != NULL && rightChild->value > largest->value) { largest = rightChild; } // 如果最大值不是当前节点,交换后继续下沉 if (largest != currentNode) { // 交换两个节点的值(如果是链表节点交换位置,逻辑类似) int tempVal = currentNode->value; currentNode->value = largest->value; largest->value = tempVal; // 递归处理被交换的子节点,确保它也满足堆性质 siftDown(heap, largest, COUNT); } }
2. 完整的Heapify函数
从最后一个非叶子节点开始,向上遍历所有父节点,逐个执行下沉操作:
void heapify(tree *heap, int COUNT) { if (COUNT <= 1) return; // 0或1个节点无需堆化 // 从最后一个非叶子节点(索引COUNT/2)开始,遍历到根节点(索引1) for (int i = COUNT / 2; i >= 1; i--) { node *targetNode = getKthNode(heap, i); siftDown(heap, targetNode, COUNT); } }
3. 插入元素后的高效堆化(上浮操作)
如果是插入新元素到已有的堆中,不用重新堆化整个堆,只需要把新元素上浮到正确位置:
void siftUp(tree *heap, node *newNode) { node *parent = newNode->parent; // 只要父节点存在且新节点值更大,就交换 while (parent != NULL && newNode->value > parent->value) { int tempVal = newNode->value; newNode->value = parent->value; parent->value = tempVal; newNode = parent; parent = newNode->parent; } }
比如插入4后,直接调用siftUp(heap, newNode4),就能快速把4上浮到根节点,得到4-3-2-1的正确结果。
验证效果
用修正后的代码处理1-2-3-4时:
- 先处理最后一个非叶子节点(值为2),和子节点4交换,得到
1-4-3-2; - 接着处理根节点(值为1),和子节点4交换,得到
4-1-3-2; - 最后对交换后的节点1执行下沉,和子节点3交换,最终得到符合要求的最大堆
4-3-2-1。
核心逻辑就是要确保每个节点都被检查到,并且交换后递归处理子节点,这样才能完成完整的堆化。
内容的提问来源于stack exchange,提问作者Kenje Hofileña

