堆排序有两个侧重点,一个是 “堆”,一个是“排序”
1、“堆”——建小堆
2、“排序”——将堆顶元素和堆底的最后一个元素交换,除了堆底最后一个元素外,调整剩下的元素
目录
一、提出问题
假设现在有一个无序的数组
int arr[] = { 70,56,30,24,33,10,75,68,90,21};
现在要在不额外开辟空间的情况下,将这个数组转化为堆
二、思路分析
1、画二叉树
堆在物理实现上使用的是顺序容器,但是在逻辑分析中使用的是二叉树
所以我们的第一步是将改数组 画 成二叉树的样子
2、调整最底部的子树
我们的目的是建小堆,父亲要比孩子节点小,否则,交换顺序,然后继续向下调整
我们这里标注一下最底部子树的双亲节点的位置
第 i 个孩子节点的双亲节点位置
parent = (i -1)/2
最后一个节点的位置 是数组的最后一个元素,即
i = sizeof(arr)/sizeof(arr[0]) - 1
所以
parent = (sizeof(arr)/sizeof(arr[0]) - 1 -1)/2
3、调整前一个子树
由于是顺序存储,只需要parent-- 就可以向前移动一个位置
和上面一样的方法,向下调整
继续向前调整,这个时候要调整的是 56 下面的所有子树
但是 56 的子树都是我们已经调整过的小堆,受到影响的只是 56 的左右孩子中更小的那个(左图)
很显然,56的右孩子更小,那就要把 21 往上调(中图)
原本子树的顺序也被打乱了,要继续向下调整,33 和 56 交换顺序(右图)
从上面可以看出
只要根的左右子树满足 小堆,受到影响的只是 孩子更小的那一条路径,另一条路径不受影响
三、代码实现
1、向下调整代码
我们在处理堆底的子树时,最先要做的,就是向下调整,向下调整的本质就是通过父结点找到子结点,然后和更小的那一个子结点交换
向下调整的思路:
(1)求出左孩子的下标,child = parent*2 + 1(默认左孩子更小,因为右孩子不一定存在)
(2)在有右孩子的前提下,跟右孩子比较,如果右孩子更小,child++,因为child代表更小的那个孩子的下标;如果没有右孩子或者右孩子比左孩子大,那么child指向的依然是左孩子
(3)得到更小的那个孩子以后,用左右孩子中更小的节点和父亲节点比较,
如果父亲节点更大,则交换顺序
如果父亲节点更小,则说明无需交换顺序,直接跳出循环
向下调整的停止条件:
(1)父亲节点的数 < 左右孩子中更小的那个数(即无需调整)
(2)左孩子/右孩子 到达最后一个节点,即child > size
(这里的size表示树的节点总个数 或 数组元素的个数)
//交换位置
void Swap(int& x,int& y)
{
int tmp = x;
x = y;
y= x;
}
//向下调整
void AdjustDown(int* a, int parent,int size)
{
//计算左孩子的下标,则右孩子的下标为 child + 1
int child = parent*2 + 1;
//判断左孩子是否到达最后一个节点
while(child < size)
{
//child表示更小的那个孩子,默认是左孩子更小
//判断是否 有右孩子,如果右孩子大于左孩子,child需要指向右孩子,即child+1
if(child + 1 < size && a[child+1] < a[child])
{
child++;
}
//父亲节点如果比 更小的那个孩子 还大,则交换位置
if(a[parent] > a[child])
{
Swap(a[parent],a[child]);
}
else
{
//如果父亲节点比 更小的那个孩子 还要小,那就无需调整
break;
}
}
}
2、建堆 代码实现
现在对于每一个子树的向下调整,我们已经写好了
现在只需要移动 parent 的位置,AdjustDown函数会为我们调整子树
停止建堆条件:parent < 0,即parent = -1
void CreateHeap(int* a,int size)
{
//最后一个节点的位置是size-1,则可以得到双亲节点的位置
int parent = (size-1-1)/2;
while(parent >= 0)
{
//调整子树
AdjustDown(a,parent,size);
//调整完以后,parent向前移动
parent--;
}
}
3、测试
int main() {
int arr[] = { 70,56,30,24,33,10,75,68,90,21};
CreateHeap(arr, sizeof(arr) / sizeof(arr[0]));
for (size_t i = 0; i < sizeof(arr) / sizeof(arr[0]); i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
测试结果:
以上就是本次的全部内容,如果对你有帮助的话,还请点个赞鼓励一下,如果有错,欢迎指正