【重新上本科】堆排序【下】
2012-08-21 15:51
155 查看
堆调整函数解决的问题是:当除了堆顶以外的元素都满足堆的条件,如何调整堆顶元素,使整个序列满足堆的条件。堆的条件,是啥?其实堆是棵二叉树,满足父节点的数值比两个儿子节点的数值大。那么如何解决上述问题呢?思路也很简单:将父节点与两个儿子节点比较,找到最大的那个,放到堆顶,将原来堆顶元素放到最大元素的原来位置,然后对这颗子树递归调用堆调整函数。
更一般地说,对于堆中第i个节点,如何调整堆中的元素,满足堆的条件?代码如下:
void MaxHeapfy (int Array[], int i, int iHeapSize) // MaxHeapfy表示维护一个最大堆
{
int l = 2 * i + 1; // 定位到i的左儿子,为啥这样定位?请翻书看下堆的数组存储
int r = 2 * i + 2; // 定位到i的右儿子
int iMax;
// 这段代码有意思,有看头,其功能是比较元素i和他的两个儿子,找到其中最大的那个,将他的位置记录到iMax中。
// 为啥左儿子的判断既有if又有else,右儿子不用?每个判断都有两个条件,前面是递归是否终止,后面是比较大小。何时、哪个条件不满足,会跳出if条件?这个地方想着简单,不过写的时候,debug了一会儿,才把代码写对。个中细节,还是debug的时候体会吧。
if (l < iHeapSize && Array[l] > Array[i])
iMax = l;
else
iMax = i;
if (r < iHeapSize && Array[r] > Array[iMax])
iMax = r;
if (iMax != i) // 如果堆顶已经是最大元素,则不用调整了,否则,交换堆顶与iMax位置的元素
{
int iTemp; // 交换元素
iTemp = Array[i];
Array[i] = Array[iMax];
Array[iMax] = iTemp;
MaxHeapfy (iMax, iHeapSize); // 递归,对子树重新建立堆
}
}
思路和代码都很简单,需要细想一想的地方就是上面“有看头”的那部分代码。难点主要在于,将选取三个数的最大者与递归终止条件混在一起判断了。
利用上面的调整函数,建立堆就很容易了。堆是用数组存储的,越往后,越是底层节点;接上文,父节点和儿子节点的关系可以用l = 2 * i + 1和l = 2 * i + 2;计算。树是从底向上建立的,倒数第二层节点的位置在数组的中部,就从这里开始。代码如下:
void BuildMaxHeap (int Array[], int iHeapSize)
{
if (iHeapSize <= 1) // 防御性代码
return;
int i = iHeapSize / 2; // 定位到数组中间,这部分是叶子节点的上一层,在此基础上建立二叉树(叶子节点自身就是二叉树了)
for (int j=i; j>=0; j--) // 从后向前遍历
MaxHeapfy (j, iHeapSize); // 自底向上建立堆,直到堆顶(位于数组最前端)
}
好,完事。
更一般地说,对于堆中第i个节点,如何调整堆中的元素,满足堆的条件?代码如下:
void MaxHeapfy (int Array[], int i, int iHeapSize) // MaxHeapfy表示维护一个最大堆
{
int l = 2 * i + 1; // 定位到i的左儿子,为啥这样定位?请翻书看下堆的数组存储
int r = 2 * i + 2; // 定位到i的右儿子
int iMax;
// 这段代码有意思,有看头,其功能是比较元素i和他的两个儿子,找到其中最大的那个,将他的位置记录到iMax中。
// 为啥左儿子的判断既有if又有else,右儿子不用?每个判断都有两个条件,前面是递归是否终止,后面是比较大小。何时、哪个条件不满足,会跳出if条件?这个地方想着简单,不过写的时候,debug了一会儿,才把代码写对。个中细节,还是debug的时候体会吧。
if (l < iHeapSize && Array[l] > Array[i])
iMax = l;
else
iMax = i;
if (r < iHeapSize && Array[r] > Array[iMax])
iMax = r;
if (iMax != i) // 如果堆顶已经是最大元素,则不用调整了,否则,交换堆顶与iMax位置的元素
{
int iTemp; // 交换元素
iTemp = Array[i];
Array[i] = Array[iMax];
Array[iMax] = iTemp;
MaxHeapfy (iMax, iHeapSize); // 递归,对子树重新建立堆
}
}
思路和代码都很简单,需要细想一想的地方就是上面“有看头”的那部分代码。难点主要在于,将选取三个数的最大者与递归终止条件混在一起判断了。
利用上面的调整函数,建立堆就很容易了。堆是用数组存储的,越往后,越是底层节点;接上文,父节点和儿子节点的关系可以用l = 2 * i + 1和l = 2 * i + 2;计算。树是从底向上建立的,倒数第二层节点的位置在数组的中部,就从这里开始。代码如下:
void BuildMaxHeap (int Array[], int iHeapSize)
{
if (iHeapSize <= 1) // 防御性代码
return;
int i = iHeapSize / 2; // 定位到数组中间,这部分是叶子节点的上一层,在此基础上建立二叉树(叶子节点自身就是二叉树了)
for (int j=i; j>=0; j--) // 从后向前遍历
MaxHeapfy (j, iHeapSize); // 自底向上建立堆,直到堆顶(位于数组最前端)
}
好,完事。
相关文章推荐
- 【重新上本科】堆排序【上】
- 【重新上本科】快速排序【上】
- 【重新上本科】快速排序【中】
- 【重新上本科】快速排序【下】
- 【重新上本科】冒泡排序
- 【重新上本科】再写main函数
- 【重新上本科】关于“static”关键字的点滴
- 【重新上本科】从一道笔试题来看数组和指针的区别(c++)
- 【重新上本科】字符串匹配算法汇总
- 重新教自己学算法之递归排序——堆排序(六)
- 【重新上本科】在实际问题中,内存赋值所拖累的效率(java版本)
- 算法思路重新实现-堆排序 中的 C++ & Java
- 【重新上本科】在实际问题中,内存赋值所拖累的效率(c++版本)
- 【重新上本科】排序【调试环境】
- Office 2007重新输入序列号激活
- 错误 frm-40654 记录已经被另一个用户更新,重新查询以查看修改
- css 重新学习系列(3)
- 选择排序,冒泡排序,归并排序,快速排序,堆排序等等
- 引起快照重新初始化的原因
- 白话经典算法系列之七 堆与堆排序