搜索树和链表实现的堆

目的:
1、创建最大堆类。最大堆的存储结构使用链表。
2、提供操作:堆的插入、堆的删除。堆的初始化。Huffman 树的构造。二叉搜索树的构造。
3、接收键盘录入的一系列整数,输出其对应的最大堆、Huffman 编码以及二叉搜索树。
4、堆排序。

堆是完全二叉树,通常是用数组实现的。但是在这里题目有要求,所以我们要思考链表如何保持完全二叉树的结构。
换句话说我们就是要找到完全二叉树最后一个元素的位置,这样我们就可以在插入和删除操作时保证堆依然是完全二叉树。
因为二叉树本身就是递归定义的结构,完全二叉树的子树也是完全二叉树,所以可以用一种递归方式,确定最后一个元素在左子树还是右子树中,直到不存在子树为止。这种计算过程需要知道堆有几个元素,所以可以给堆一个size属性记录元素个数。计算方法如下:

HeapNode *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里的方法。

二叉搜索树就比较简单了,只要小的就放左边,大的放右边就可以了,可以直接看代码。

源代码:HeapAndBSTree.zip

你可能感兴趣的:(数据结构)