目的:
1、创建最大堆类。最大堆的存储结构使用链表。
2、提供操作:堆的插入、堆的删除。堆的初始化。Huffman 树的构造。二叉搜索树的构造。
3、接收键盘录入的一系列整数,输出其对应的最大堆、Huffman 编码以及二叉搜索树。
4、堆排序。
堆是完全二叉树,通常是用数组实现的。但是在这里题目有要求,所以我们要思考链表如何保持完全二叉树的结构。
换句话说我们就是要找到完全二叉树最后一个元素的位置,这样我们就可以在插入和删除操作时保证堆依然是完全二叉树。
因为二叉树本身就是递归定义的结构,完全二叉树的子树也是完全二叉树,所以可以用一种递归方式,确定最后一个元素在左子树还是右子树中,直到不存在子树为止。这种计算过程需要知道堆有几个元素,所以可以给堆一个size属性记录元素个数。计算方法如下:
HeapNode<T> *p = root,//指向当前节点的指针
*pp = 0;//指向当前节点的父节点的指针
int tsize = size; //将元素数保存到一个临时变量中
size--;
int height = 2; //下一层的元素个数
while (tsize >= height * 2) {
height *= 2;
}
while (height > 1) {//一直进行到层高为1,即没有子节点为止
pp = p;
if (tsize < height * 3 / 2) {//最后一个元素在左子树中
p = p->leftChild;
tsize -= height / 2;
}
else {//最后一个元素在右子树中
p = p->rightChild;
tsize -= height;
}
height /= 2;
}
进行完上述过程后,p就指向最后一个元素,pp就指向了它的父节点,其他步骤就只要像数组实现的堆一样实现就可以了。
插入时如果不想像数组实现一样从下往上,也可以把插入过程融合到上述过程里,具体情况可以看我发在最后的代码。
堆的初始化在链表实现中也不是很简单,主要是链表并不像数组一样可以简单的进行层序访问。但是,可以采用数组保存前size/2个节点,并对该数组中的元素倒序进行调整,从而满足O(n)的时间复杂度。
template<class T>
void MaxHeap<T>::Initialize(T a[], int size)
{
ClearHeap(root); //清空原先的树
this->size = size;
if (size == 0) return;
HeapNode<T> **insideNode = new HeapNode<T>*[size / 2]; //定义一个保存前size/2个节点的数组
HeapNode<T> *currentNode;
HeapNode<T> *tmp;//存放当前放入元素的节点
int index = 0; //insideNode数组的索引
//初始化根节点
root = new HeapNode<T>(a[0]);
insideNode[0] = root;
currentNode = root;
for (int i = 1; i < size; i++) {
if (!currentNode->leftChild) {//左节点为空
currentNode->leftChild = new HeapNode<T>(a[i]);
tmp = currentNode->leftChild;
}
else if (!currentNode->rightChild) {//右节点为空
currentNode->rightChild = new HeapNode<T>(a[i]);
tmp = currentNode->rightChild;
}
else {//左右节点都满
index++;
currentNode = insideNode[index];
currentNode->leftChild = new HeapNode<T>(a[i]);
tmp = currentNode->leftChild;
}
if (i < size / 2) //如果数组元素下标小于size/2,则放入insideNode数组中
insideNode[i] = tmp;
}
HeapNode<T> *pp = 0;//当前节点的上一个节点
//对interNode中的节点从最后一个开始进行调整
for (; index >= 0; index--) {
tmp = insideNode[index];
T y = tmp->data;
while (tmp) {
pp = tmp;
if (tmp->rightChild&&tmp->leftChild->data < tmp->rightChild->data)
tmp = tmp->rightChild;
else
tmp = tmp->leftChild;
if (!tmp || y > tmp->data) break;//当tmp为空或y比tmp内元素大时跳出
pp->data = tmp->data;
}
pp->data = y;
}
delete[] insideNode;
}
霍夫曼树,即有最小WEP(加权外部路径长度,weighted external path length)的树,可以用于生成元素的霍夫曼编码。就是把两个权重最小的数构成一棵新的树,放回序列中,再重复这个过程,直到只剩下一棵树,通过对这棵树进行解析就可以得到霍夫曼编码了。因为是要取最小数,可以将权重加上负号而使用最大堆,也可以再写一个最小堆。解析方式可以参考我放在最后的代码中的BinaryTree.cpp里的方法。
二叉搜索树就比较简单了,只要小的就放左边,大的放右边就可以了,可以直接看代码。