
提示文章写完后目录可以自动生成如何生成可参考右边的帮助文档文章目录前言一、什么是堆二、向上调整建堆1. 算法思想2. 步骤演示大顶堆3. 代码实现C语言4. 时间复杂度分析三、向下调整建堆1. 算法思想2. 步骤演示大顶堆3. 代码实现C语言4. 时间复杂度分析四、两种方法的对比与选择五、在堆排序中的应用总结前言提示这里可以添加本文要记录的大概内容在数据结构与算法中堆Heap是一种非常重要的完全二叉树结构常用于实现优先队列和堆排序。堆排序的核心在于如何高效地构建一个堆而建堆过程主要分为两种策略向上调整Sift Up建堆和向下调整Sift Down建堆。本文将详细解析这两种建堆方法的原理、步骤、时间复杂度以及适用场景并通过代码示例帮助读者深入理解。提示以下是本篇文章正文内容下面案例可供参考一、什么是堆堆是一种特殊的完全二叉树满足以下性质之一大顶堆Max Heap每个节点的值都大于或等于其子节点的值根节点是最大值。小顶堆Min Heap每个节点的值都小于或等于其子节点的值根节点是最小值。堆通常用数组来实现对于数组中下标为i的节点:(这里第一个元素的下标为0,有一些教材上是以1作为第一个元素的下标因此公式会不同)父节点下标parent(i) (i - 1) / 2左子节点下标left(i) 2 * i 1右子节点下标right(i) 2 * i 2二、向上调整建堆1. 算法思想向上调整建堆采用增量插入的策略。假设初始时堆为空我们依次将每个新元素插入到数组末尾然后通过“向上调整”操作使其满足堆的性质。使用前提:如果要使用该算法建大堆,则必须要保证,该堆初始已经是大堆(小堆同理)。向上调整从当前节点开始与其父节点比较。如果当前节点值大于父节点大顶堆则交换两者位置然后继续向上比较直到到达根节点或满足堆性质为止。2. 步骤演示大顶堆假设要构建的数组为[3, 9, 2, 1, 4, 5]插入 3堆为[3]插入 9[3, 9]→ 9 3交换 →[9, 3]插入 2[9, 3, 2]→ 2 3父节点无需调整插入 1[9, 3, 2, 1]→ 1 3无需调整插入 4[9, 3, 2, 1, 4]→ 4 3交换 →[9, 4, 2, 1, 3]→ 4 9停止插入 5[9, 4, 2, 1, 3, 5]→ 5 2交换 →[9, 4, 5, 1, 3, 2]→ 5 9停止最终大顶堆[9, 4, 5, 1, 3, 2]3. 代码实现C语言// 向上调整大顶堆voidsiftUp(intheap[],intindex){while(index0){intparent(index-1)/2;//找出index的双亲节点if(heap[index]heap[parent]){break;}// 交换inttempheap[index];heap[index]heap[parent];heap[parent]temp;indexparent;}}// 向上调整建堆voidbuildHeapBySiftUp(intarr[],intn){//从第2个节点开始调用向上调整建堆,以确保再i之前的堆已经是大堆for(inti1;in;i){siftUp(arr,i);}}4. 时间复杂度分析单个元素向上调整的最坏时间复杂度为 O(logn)对 n 个元素建堆总时间复杂度为O(n*logn)三、向下调整建堆1. 算法思想向下调整建堆采用整体调整的策略。从最后一个非叶子节点开始向前遍历每个节点对每个节点执行“向下调整”操作使其子树满足堆性质。使用前提:如果要使用该算法建大堆,则在某个根节点后的所有子节点都必须已经是大堆(小堆同理)。向下调整从当前节点开始与其左右子节点中较大大顶堆的那个比较。如果当前节点值小于该子节点则交换两者位置然后继续向下调整直到到达叶子节点或满足堆性质为止。2. 步骤演示大顶堆同样以数组[3, 9, 2, 1, 4, 5]为例找到最后一个非叶子节点下标lastNonLeaf (n/2) - 1 2从下标 2值为 2开始向前调整节点 2值 2子节点为 5下标 55 2交换 →[3, 9, 5, 1, 4, 2]节点 1值 9子节点为 1 和 49 最大无需调整节点 0值 3子节点为 9 和 59 最大3 9交换 →[9, 3, 5, 1, 4, 2]继续向下调整节点 1值 3子节点为 1 和 44 最大3 4交换 →[9, 4, 5, 1, 3, 2]最终大顶堆[9, 4, 5, 1, 3, 2]与向上调整结果相同3. 代码实现C语言// 向下调整大顶堆voidsiftDown(intheap[],intn,intindex){intlargestindex;while(1){intleft2*index1;intright2*index2;if(leftnheap[left]heap[largest]){largestleft;}if(rightnheap[right]heap[largest]){largestright;}if(largestindex){break;}// 交换inttempheap[index];heap[index]heap[largest];heap[largest]temp;indexlargest;}}// 向下调整建堆Floyd 建堆算法voidbuildHeapBySiftDown(intarr[],intn){// 从最后一个非叶子节点开始向前调整,确保所有子节点都已经是大堆for(intin/2-1;i0;i--){siftDown(arr,n,i);}}4. 时间复杂度分析单个元素向下调整的最坏时间复杂度为 O(logn)向下调整建堆Floyd算法的总时间复杂度为O(n)四、两种方法的对比与选择特性向上调整建堆向下调整建堆建堆策略增量插入从前往后整体调整从后往前起始点第二个元素开始最后一个非叶子节点开始时间复杂度O(n*logn)O(n)空间复杂度O(1)原地O(1)原地适用场景流式数据、动态插入静态数组、一次性建堆优势实现简单适合动态维护效率更高适合批量构建选择建议如果需要动态插入元素并随时保持堆性质如优先队列使用向上调整如果已有完整数组需要一次性构建堆如堆排序使用向下调整Floyd算法五、在堆排序中的应用堆排序通常使用向下调整建堆因为它的时间复杂度更低// 堆排序升序使用大顶堆voidheapSort(intarr[],intn){// 1. 构建大顶堆向下调整buildHeapBySiftDown(arr,n);// 2. 逐个将堆顶元素移到末尾for(intin-1;i0;i--){// 交换堆顶和当前末尾元素inttemparr[0];arr[0]arr[i];arr[i]temp;// 调整剩余元素使其保持堆性质siftDown(arr,i,0);}}总结提示这里对文章进行总结向上调整建堆和向下调整建堆是构建堆的两种基本方法向上调整建堆O(n*logn)从第二个元素开始逐个插入并向上调整适合动态插入场景向下调整建堆O(n)从最后一个非叶子节点开始向前调整效率更高适合静态数组的一次性构建在实际应用中堆排序和优先队列的实现通常优先选择向下调整建堆Floyd算法以获得更好的时间复杂度。