题目
阅读下列说明和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]为升序排列)
820

被折叠的 条评论
为什么被折叠?



