堆排序中的向上调整与向下调整建堆算法详解
2026/8/9 6:36:36 网站建设 项目流程

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、什么是堆?
  • 二、向上调整建堆
    • 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]

  1. 插入 3:堆为[3]
  2. 插入 9:[3, 9]→ 9 > 3,交换 →[9, 3]
  3. 插入 2:[9, 3, 2]→ 2 < 3(父节点),无需调整
  4. 插入 1:[9, 3, 2, 1]→ 1 < 3,无需调整
  5. 插入 4:[9, 3, 2, 1, 4]→ 4 > 3,交换 →[9, 4, 2, 1, 3]→ 4 < 9,停止
  6. 插入 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(index>0){intparent=(index-1)/2;//找出index的双亲节点if(heap[index]<=heap[parent]){break;}// 交换inttemp=heap[index];heap[index]=heap[parent];heap[parent]=temp;index=parent;}}// 向上调整建堆voidbuildHeapBySiftUp(intarr[],intn){//从第2个节点开始调用向上调整建堆,以确保再i之前的堆已经是大堆for(inti=1;i<n;i++){siftUp(arr,i);}}

4. 时间复杂度分析

  • 单个元素向上调整的最坏时间复杂度为 O(logn)
  • 对 n 个元素建堆,总时间复杂度为O(n*logn)

三、向下调整建堆

1. 算法思想

向下调整建堆采用整体调整的策略。从最后一个非叶子节点开始,向前遍历每个节点,对每个节点执行“向下调整”操作,使其子树满足堆性质。

使用前提:如果要使用该算法建大堆,则在某个根节点后的所有子节点都必须已经是大堆(小堆同理)。

向下调整:从当前节点开始,与其左右子节点中较大(大顶堆)的那个比较。如果当前节点值小于该子节点,则交换两者位置,然后继续向下调整,直到到达叶子节点或满足堆性质为止。

2. 步骤演示(大顶堆)

同样以数组[3, 9, 2, 1, 4, 5]为例:

  1. 找到最后一个非叶子节点下标:lastNonLeaf = (n/2) - 1 = 2
  2. 从下标 2(值为 2)开始向前调整:
    • 节点 2:值 2,子节点为 5(下标 5),5 > 2,交换 →[3, 9, 5, 1, 4, 2]
    • 节点 1:值 9,子节点为 1 和 4,9 最大,无需调整
    • 节点 0:值 3,子节点为 9 和 5,9 最大,3 < 9,交换 →[9, 3, 5, 1, 4, 2]
      • 继续向下调整节点 1(值 3):子节点为 1 和 4,4 最大,3 < 4,交换 →[9, 4, 5, 1, 3, 2]

最终大顶堆:[9, 4, 5, 1, 3, 2](与向上调整结果相同)

3. 代码实现(C语言)

// 向下调整(大顶堆)voidsiftDown(intheap[],intn,intindex){intlargest=index;while(1){intleft=2*index+1;intright=2*index+2;if(left<n&&heap[left]>heap[largest]){largest=left;}if(right<n&&heap[right]>heap[largest]){largest=right;}if(largest==index){break;}// 交换inttemp=heap[index];heap[index]=heap[largest];heap[largest]=temp;index=largest;}}// 向下调整建堆(Floyd 建堆算法)voidbuildHeapBySiftDown(intarr[],intn){// 从最后一个非叶子节点开始向前调整,确保所有子节点都已经是大堆for(inti=n/2-1;i>=0;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(inti=n-1;i>0;i--){// 交换堆顶和当前末尾元素inttemp=arr[0];arr[0]=arr[i];arr[i]=temp;// 调整剩余元素,使其保持堆性质siftDown(arr,i,0);}}

总结

提示:这里对文章进行总结:

向上调整建堆和向下调整建堆是构建堆的两种基本方法:

  1. 向上调整建堆(O(n*logn)):从第二个元素开始,逐个插入并向上调整,适合动态插入场景
  2. 向下调整建堆(O(n)):从最后一个非叶子节点开始向前调整,效率更高,适合静态数组的一次性构建

在实际应用中,堆排序和优先队列的实现通常优先选择向下调整建堆(Floyd算法)以获得更好的时间复杂度。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询