记录作业题
一、题目要求假设定义最大堆为满足如下性质的完全三叉树:
- 空树为堆;
- 每个结点最多有3棵子树,且结点的值不小于所有子树根的值,且所有子树均为最大堆。
按上述述要求完成三叉堆的存储结构设计,再实现三叉堆的向下调整算法,最后完成利用三叉堆进行排序的算法。要求利用大数据量进行测试,同原有堆排序算法在时间上进行对比分析。
二、二叉堆代码本题算法逻辑与二叉堆代码一致,因此先放出课本上二叉堆代码。
要注意的是,长度为n的堆中各元素的序号是从0开始,到n-1结束。因此对于序号为i的节点而言,其左孩子序号为2*i+1,右孩子序号为2*i+2。
templatevoid HeapSort(ElemType elem[], int n) // 操作结果:对数组elem进行堆排序 { int i; for (i = (n-2)/2; i >= 0; --i) // 初始建堆,将elem[0 .. n - 1]调整成最大堆 FilterDown(elem, i, n - 1); for (i = n - 1; i > 0; --i) { // 第i趟堆排序 Swap(elem[0], elem[i]); // 将堆顶元素和当前未经排序的子序列elem[0 .. i]中最后一个元素交换 FilterDown(elem, 0, i - 1); // 将elem[0 .. i - 1]重新调整为最大堆 } }
template三、思路void FilterDown(ElemType elem[], int low, int high) // 操作结果:将以low为根的子树调整成为一个最大堆 { int f = low, i = 2 * low + 1; ElemType e = elem[low]; while (i <= high) { // f为被调整结点,i为f的最大孩子 if (i < high && elem[i] < elem[i + 1]) i++; // f有右孩子,且右孩子的值更大, i指向右孩子 if (e < elem[i]) { // 孩子的值大于其双亲,则进行向下调整 elem[f] = elem[i]; f = i; i = 2 * f + 1; } else break; } elem[f] = e; }
对于排序部分,三叉堆与二叉堆的整体逻辑一模一样:都是将堆中具有最大关键字的元素elem[0]与堆的最后一个元素交换位置,然后调整堆中除最后一个元素以外的元素,使之成为最大堆,再将新的elem[0]与堆的最后一个元素交换位置……不断重复此过程直到将所有元素排好序。由于逻辑相同,所以只需要根据完全三叉树的性质对相应数字做修改即可,也就是在寻找最后一个分支节点i时,采取计算公式i=(n-1)/3,其余部分代码完全相同。
三叉堆和二叉堆最大的不同之处出现在调整函数。因为三叉堆的一个根节点最多会有三个孩子,所以双亲结点与孩子结点的序号对应会有不同;在寻找被调整结点的最大的孩子时,也要比二叉堆的对应环节更加复杂。具体改变如下:
- 对于序号为f的被调整结点,其最左边的孩子序号为3*f+1。
- 为了找到三个孩子中关键字最大的孩子j,需要使用多重判断。如果最左边的孩子小于中间的孩子,则再比较中间的孩子与最右边的孩子,j表示后两者中较大的孩子;如果最左边的孩子大于中间的孩子且小于最右边的孩子,则j表示最右边的孩子;如果以上两种情况都不成立,则最左边的孩子为最大的孩子,j表示最左边的孩子。
经过以上分析,通过仿照二叉堆代码,得到三叉堆代码如下:
templatevoid HeapSort3(ElemType elem[], int n) { int i; for(i = (n-1)/3; i >= 0; --i) FilterDown3(elem, i, n - 1); for(i = n - 1; i > 0; --i) { Swap(elem[0], elem[i]); FilterDown3(elem, 0, i - 1); } }
templatevoid FilterDown3(ElemType elem[], int low, int high) { int f = low, j = 3 * low + 1; ElemType e = elem[low]; while(j <= high) { if(j < high && elem[j] < elem[j + 1]) { j++; if(j < high && elem[j] < elem[j + 1]) j++; } else if(j + 1 < high && elem[j] < elem[j + 2]) j = j + 2; if(e < elem[j]) { elem[f] = elem[j]; f = j; j = 3 * f + 1; } else break; } elem[f] = e; }
用大数据量进行测试后发现,三叉堆的排序算法比原先二叉堆排序算法的速度要更快一些。分析认为这是由于在三叉堆向下调整函数的每一次循环中,比较的节点数量更多,因此将数组化为最大堆所需的循环更少,消耗的时间也就更少。



