堆操作大全

1 篇文章 0 订阅

一、定义

1.优先级队列:
不同于队列这种数据结构,优先级队列元素出队列的顺序是由元素的优先级决定的,可以递增也可以是递减的。
2.大根树(小根树):
每个节点的值都大于(小于)或等于其子节点的值。
3.大根堆(小根堆):
既是大根树(小根数)也是完全二叉树。

二、大根堆的插入与删除操作

由于堆是完全二叉树,所以用数组描述最为高效。

1.插入:

插入操作比较简单,把新元素插入新节点,然后沿着新节点到根节点的路径,执行一次起泡操作。
实现代码:

//堆的插入方法,theElement表示优先级,  时间复杂度时O(logn)
template <class T>
void maxHeap<T>::push(const T & theElement) {
    if (heapSize == arrayLength-1){
        //数组容量不够了
        throw 3;
    }

    int currentNode = ++heapSize;
    while (currentNode != 1&&heap[currentNode/2]<theElement){
        //current的父节点刚好是current/2向下取整
        heap[currentNode] = heap[currentNode/2];
        currentNode /= 2;
    }
    heap[currentNode] = theElement;

}

时间复杂度与高度有关是O(logn)。

2.删除操作

由于删除的是优先级最高的(最低的)所以删除根节点元素,将末节点元素取出,删除末节点,将根节点左右孩子中大的放到根节点,不断重复。
实现代码:

//删除节点的方法,时间复杂度是O(logn)
template <class T>
void maxHeap<T>::pop() {
    if (heapSize == 0){
        throw 4;
    }

    heap[1].~T();//删除堆顶

    T lastElement = heap[heapSize--];//将堆的最后一个元素拿出来

    int currentNode = 1,child = 2;

    while (child <= heapSize)
    {
        //找出两个孩子中大的那一个
        if (child<heapSize&&heap[child]<heap[child+1]){
            child++;
        }
        if (lastElement>=heap[child]){
            break;//末元素找到位置了
        }

        heap[currentNode] = heap[child];
        currentNode = child;
        child = child*2;//获得大孩子的左孩子
    }

    heap[currentNode] = lastElement;

}

时间复杂度与高度有关是O(logn)。

三、堆的初始化

1.可以利用插入操作,一个一个元素的插入。复杂度是O(nlogn)

2.直接利用数组元素初始化,复杂度是Θ(n)

原理:从第一个有孩子的节点开始一次重构为最大堆。

//堆的初始化,时间复杂度为Θ(n),比一个一个的插入性能要好一些
template <class T>
void maxHeap<T>::initialize(T *theHeap, int theSize) {

    delete [] heap;
    heap = theHeap;
    heapSize = theSize;

    //从第一个有孩子的节点开始排查

    for (int root = heapSize/2; root >=1 ; --root) {

        T theElement = heap[root];

        int child = 2*root;

        while (child<=heapSize){
            if (child<heapSize&&heap[child]<heap[child+1]){
                child++;//获取大孩子
            }

            if (theElement>=heap[child]){
                break;
            }

            //父子交换
            heap[child/2] = heap[child];
            child *= 2;
        }

        heap[child/2] = theElement;

    }


}

四、应用

1.堆排序

利用元素初始化最大堆,每次取出最大元素并删除,时间复杂度为O(nlogn)
实现代码:

//堆排序在初始化和删除的基础上实现变得非常简单,事件复杂度为O(nlogn),初始化为O(n)排序为O(logn)
template <class T>
void maxHeap<T>::sort() {

    T *temp = new T[heapSize+1];

    int size = heapSize;//pop()方法导致heapsize大小在动态变化

    for (int i = 1; i <= size ; ++i) {
        T a = top();
        pop();
        temp[i] = a;
    }

    heap = temp;
    //由于pop方法将size的大小减到了0所以必须将大小还原。
    heapSize = size;

//    cout<<heapSize<<"***********************"<<endl;

}
2.霍夫曼编码

实现代码:

template <class T>
linkedBinaryTree<int>* huffmanTree(T weight[],int n){

    huffmanNode<T> *hNode = new huffmanNode<T>[n+1];
    linkedBinaryTree<int> emptyTree;

    //创建不同频率的霍夫曼节点
    for (int i = 1; i <= n ; ++i) {
        hNode[i].weight = weight[i];//频率是从数组的下标一开始计算的
        hNode[i].tree = new linkedBinaryTree<int>;
        hNode[i].tree->makeTree(i,emptyTree,emptyTree);
    }

    //使一组单节点树构成小根堆
    minHeap<huffmanNode<T>> heap(1);
    heap.initialize(hNode,n);

    huffmanNode<T> w,x,y;
    linkedBinaryTree<int> *z;

    for (int j = 1; j < n ; ++j) {

        //取出最小的两棵树
        x = heap.top();heap.pop();
        y = heap.top();heap.pop();

        //将他们合成一棵树
        z = new linkedBinaryTree<int>();
        z->makeTree(0,*x.tree,*y.tree);
        w.weight = x.weight+y.weight;
        w.tree = z;

        //将合成的树放到堆中
        heap.push(w);

        delete x.tree;
        delete y.tree;
    }

    return heap.top().tree;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值