软考中级习题与解答——堆排序

题目

阅读下列说明和C代码,回答问题1至问题3,将解答填入答题纸的对应栏内。
【说明】
排序是将一组无序的数据元素调整为非递减顺序的数据序列的过程,堆排序是一种常用的排序算法。用顺序存储结构存储堆中元素,非递减堆排序的步骤是:
(1)将含n个元素的待排序数列构造成一个初始大项堆,存储在数组R(R[1],R[2],… R[n])中。此时堆的规模为n,堆项元素R[1]就是序列中最大的元素,R[n]是堆中最后一个元素。
(2)将堆顶元素和堆中最后一个元素交换,最后一个元素脱离堆结构,堆的规模减1,将堆中剩余的元素调整成大顶堆;
(3)重复步骤(2),直到只剩下最后一个元素在堆结构中,此时数组R是一个非递减的数据序列。
【C代码】
下面是该算法的C语言实现。
(1)主要变量说明
n:待排序的数组长度
R[]:待排序数组,n个数放在R[1],R[2],…,R[n]中
(2)代码

#include
#define MAXITEM 100
 
/*
调整堆
R:待排序数组;
v:节点编号,以v为根的二叉树,R[v]≥R[2v],R[v]≥R[2v+1],且其左子树和右子树都是大顶堆;
n:堆结构的规模,即堆中的元素数
*/
void Heapify(int R[MAXITEM], int v, int n){
    inti,j;
i=v;
j=2*i;

R[0]=R[i];
while(j<=n){
    if(j<n&&R[j]<R[j+1]){
    j++;
}
if(    (1)    ){
    R[i]=R[j];
i=j;
j=2*i;
}
else{
    j=n+1;
}
}
R[i]=R[0];
}
 
/* 堆排序,R为待排序数组;n为数组大小  */
void HeapSort(int R[MAXITEM], int n){
    int i;

for(i=n/2; i>=1; i--){
    (2)    ;
    }
for(i=n;    (3)    ;i--){
R[0]=R[i];
R[i]=R[1];
    (4)    ;
Heapify(R,1,i-1);
}
}

解题思路框架

1. 理解算法逻辑

  • 堆排序分两阶段:建堆 + 排序

  • 建堆:从最后一个非叶子节点开始调整

  • 排序:将堆顶元素与堆尾交换,然后调整剩余堆

2. 分析函数功能

Heapify函数:调整以v为根的子树为大顶堆

  • 从根节点开始,与较大的子节点比较

  • 如果根节点小就下沉,继续调整

HeapSort函数

  • 第一个for循环:建堆

  • 第二个for循环:排序


知识点分析

什么是建堆?

建堆就是把一个无序的数组调整成满足"堆性质"的完全二叉树。

建堆的顺序:从下往上,从右往左

堆的性质(大顶堆):

  • 父节点 ≥ 子节点

  • 只是一个父子的大小关系,不是排序

举例说明:

原始数组[0, 5, 2, 8, 3, 1](R[1]=5, R[2]=2, R[3]=8, R[4]=3, R[5]=1)

对应的完全二叉树

    5
   / \
  2   8
 / \
3   1
建堆过程详解

第一步:找到最后一个非叶子节点

  • 最后一个节点位置:5

  • 它的父节点:5/2 = 2(向下取整)

  • 所以从位置2开始调整

如何找到最后一个非叶子节点?

在完全二叉树中:

  • 最后一个非叶子节点的位置 = n/2(向下取整)

  • 其中 n 是节点总数

第二步:调整位置2(R[2]=2)

    5
   / \
  2   8   ← 调整以2为根的子树
 / \
3   1

比较:2 vs max(3,1) = 3
因为 2 < 3,所以交换:

    5
   / \
  3   8
 / \
2   1

第三步:调整位置1(R[1]=5)

顺序为索引递减,如果n=8的话就是

循环:for(i = n/2; i >= 1; i--) = for(i = 4; i >= 1; i--)

执行顺序4 → 3 → 2 → 1(索引递减)

当前是n=5,最后一个非叶节点是2,所以下一个位置是1。

    5      ← 调整以5为根的子树
   / \
  3   8
 / \
2   1

比较:5 vs max(3,8) = 8
因为 5 < 8,所以交换:

    8
   / \
  3   5
 / \
2   1

现在就是一个大顶堆了,完成建堆的步骤。建堆后的数组[0, 8, 3, 5, 2, 1]


排序阶段详解

排序阶段的核心思想:每次将堆顶(最大值)与堆尾交换,然后调整剩余部分为新堆

第一次循环 (i=5)

步骤1:交换堆顶和堆尾

交换后数组[0, 1, 3, 5, 2, 8]

    1      ← 新的堆顶
   / \
  3   5
 / \
2   8      ← 已排序部分

当前堆:只包含前4个元素 [1, 3, 5, 2],堆规模为4

步骤2:调整剩余堆 (Heapify(R, 1, 4))

调整前4个元素:[1, 3, 5, 2]

    1
   / \
  3   5
 /
2

比较:1 vs max(3,5) = 5 → 1 < 5,交换
    5
   / \
  3   1
 /
2

第二次循环 (i=4)

步骤1:交换堆顶和堆尾

  • 堆顶R[1] = 5,堆尾R[4] = 2

  • 交换:5 ↔ 2

交换后数组[0, 2, 3, 1, 5, 8]

    2      ← 新的堆顶
   / \
  3   1
 / \
[5] [8]    ← 已脱离堆结构

步骤2:调整剩余堆 (Heapify(R, 1, 3))

    2
   / \
  3   1

比较:2 vs max(3,1) = 3 → 2 < 3,交换
    3
   / \
  2   1

第三次循环 (i=3)

步骤1:交换堆顶和堆尾

  • 堆顶R[1] = 3,堆尾R[3] = 1

  • 交换:3 ↔ 1

交换后数组[0, 1, 2, 3, 5, 8]

    1      ← 新的堆顶
   / \
  2   [3]  ← 已脱离堆结构
 / \
[5] [8]

步骤2:调整剩余堆 (Heapify(R, 1, 2))

    1
   / 
  2

比较:1 vs 2 → 1 < 2,交换
    2
   / 
  1

第四次循环 (i=2)

步骤1:交换堆顶和堆尾

  • 堆顶R[1] = 2,堆尾R[2] = 1

  • 交换:2 ↔ 1

交换后数组[0, 1, 2, 3, 5, 8]

    1      ← 新的堆顶
   / \
 [2] [3]   ← 已脱离堆结构
 / \
[5] [8]
最终排序结果

完全有序数组[0, 1, 2, 3, 5, 8](R[1]到R[5]为升序排列)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值