每日一题24:堆

本文记录了使用C++模板实现了堆的基本操作,对于其他一些有用操作如IncreaseKey和DecreaseKey等则没有实现,这是因为使用模板把最小堆和最大堆揉在一起,对Key的增减我还没有找到比较好的处理方式,而现在写这个堆数据结构主要是因为在Hoffman树算法需要,基本操作已经够用了。
堆是一棵完全二叉树,所谓完全二叉树就是一棵从上倒下,从左到右依次填满每一个位置的二叉树,除了最后一层节点没有子节点外,最多只有倒数第二层后面部分节点没有子节点或只有一个节点(最多只有一个节点仅有一个子节点,这个节点后的节点没有子节点,而之前的节点都有两个子节点)。这棵树的元素是放在一个数组中的,父子关系通过数组下标来维护,如果从数组(下标从0开始)的第二个位置存放元素,那么某个元素A[i]的左孩子将被放到A[2*i],而右孩子放在A[2*i+1],父节点则放在A[i/2]。如果从数组(下标从0开始)的第一个位置存放元素,那么某个元素A[i]的左孩子将被放到A[2*i+1],而右孩子放在A[2*i+2],父节点则放在A[(i – 1)/2]。
最小堆中每个节点的Key值都不大于它的孩子(如果有的话),同理,最大堆中每个节点的Key值都不小于它的孩子(如果有的话)。概念完毕,看代码:

#ifndef _HEAP_H_
#define _HEAP_H_

#include "../include/Functor.h"
#include "../Utilites/type_traits.h"
#include "Vector.h"
namespace MyDataStructure
{
    //堆数据结构,默认为最大堆,需要最
    //小堆,只需指定比函数为大于比较
    template<typename ValueType,typename Compare = less<ValueType>>
    class Heap
    {
    public:
        typedef typename ParameterTrait<ValueType>::ParameterType ParameterType;
        typedef typename Heap<ValueType> self;
    public:
        Heap(){};
        Heap(int capacity);
        Heap(ValueType values[], int count);
        Heap(const Heap& rhs);
        self& operator = (const Heap& rhs);
        ~Heap(){}
        void Insert(const ParameterType value);
        bool GetTop(ValueType& top);
        void RemoveTop();
        void Clear(){ values.Clear(); }
        int Size(){ return values.Size(); }
        int Capacity(){ return values.Capacity(); }
    private:
        void copy(const Vector<ValueType>& values);
        void clear();
        void sift_down(int start,int end);
        void sift_up(int start);
        void build_heap();
    private:
        //使用Vector以支持动态扩容
        Vector<ValueType> values;
        Compare comp;
    };

    template<typename ValueType, typename Compare>
    Heap<ValueType, Compare>::Heap(int capacity)
    {
        values.Resize(capacity);
    }

    template<typename ValueType, typename Compare>
    Heap<ValueType, Compare>::Heap(ValueType values[], int count)
    {
        for (int i = 0; i < count; ++i)
        {
            this->values.PushBack(values[i]);
        }
        build_heap();
    }

    template<typename ValueType,typename Compare>
    Heap<ValueType, Compare>::Heap(const Heap& rhs)
    {
        clear();
        copy(rhs.values);
    }

    template<typename ValueType, typename Compare>
    typename Heap<ValueType,Compare>::self&
    Heap<ValueType, Compare>
    ::operator=(const Heap<ValueType, Compare>& rhs)
    {
        clear();
        copy(rhs.values);
        return *this;
    }

    template<typename ValueType, typename Compare>
    void Heap<ValueType, Compare>::Insert(const ParameterType value)
    {
        values.PushBack(value);
        sift_up(values.Size() - 1);
    }

    template<typename ValueType, typename Compare>
    bool Heap<ValueType, Compare>::GetTop(ValueType& top)
    {
        if(values.Size() <= 0) return false;
        else
        {
            top = values[0];
            return true;
        }
    }

    template<typename ValueType, typename Compare>
    void Heap<ValueType, Compare>::RemoveTop()
    {
        int size = values.Size();
        if (size <= 0) return;
        ValueType temp = values[0];
        //将最后一个元素覆蓋到第一个元素
        values[0] = values[size - 1];
        //删除最后一个元素
        values.Erase(size - 1);
        --size;
        //对第一个元素执行下滤操作
        sift_down(0,size - 1);
    }

    template<typename ValueType, typename Compare>
    void Heap<ValueType, Compare>::copy(const Vector<ValueType>& values)
    {
        this->values = values;
    }

    //清除堆中数据,并不需要销毁占用的内存
    template<typename ValueType, typename Compare>
    void Heap<ValueType, Compare>::clear()
    {
        values.Clear();
    }

    //下滤(以最大堆为例),从某个元素x开始,分别将其与他的左右孩子比较,
    //如果小于其中任意一个,那么就把三者中最小的一个覆蓋x,然后假设x被放到了
    //之前三者中最小元素所在的位置上,重复上面的过程,直到x大于或等于他的
    //左右孩子或到达了堆的末尾,然后把x放到最新空出来的位置上
    template<typename ValueType, typename Compare>
    void Heap<ValueType, Compare>::sift_down(int start,int end)
    {
        int i = start, j = 2 * i + 1;
        ValueType temp = values[i];
        while ( j <= end)
        {
            if (j < end && comp(values[j], values[j + 1])) ++j;
            if (!comp(temp,values[j])) break;
            else
            {
                values[i] = values[j];
                i = j;
                j = 2 * i + 1;
            }
        }
        values[i] = temp;
    }

    //上滤(以最大堆为例),从某个元素x开始,如果其比父节点还大,
    //那么就把父节点覆蓋到x的位置上,然后假设x被放到父节点的位置,
    //重复上面的过程,直到x大于或等于他的左右孩子
    //或到达了堆顶,然后把x放到最新空出来的位置上
    template<typename ValueType, typename Compare>
    void Heap<ValueType, Compare>::sift_up(int start)
    {
        int j = start,i = (j - 1) / 2;
        ValueType temp = values[j];
        while (j > 0)
        {
            if (!comp(values[i],temp)) break;
            else
            {
                values[j] = values[i];
                j = i;
                i = (j - 1) / 2;
            }
        }
        values[j] = temp;
    }

    //建立堆(以最大堆为例),从堆的中间位置(堆中元素
    //有偶数个时,取下中位数,为基数时,取中位数的前一个)
    //开始向前对每个元素执行下滤操作。中间位置后的元素一定是
    //前面元素的孩子,所以后半段中如果有较小的元素一定会被虑
    //上来的,同时较大的元素会被下放
    template<typename ValueType, typename Compare>
    void Heap<ValueType, Compare>::build_heap()
    {
        int end = values.Size() - 1;
        for (int i = (end - 1) / 2; i >= 0; --i)
        {
            sift_down(i, end);
        }
    }
}

#endif

下面利用利用堆排序来测试上面所列的各种操作:

// HeapTest.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "../include/Heap.h"
#include "../include/Functor.h"
#include <iostream>

using namespace MyDataStructure;
using namespace std;

//使用堆结构进行了排序操作

int _tmain(int argc, _TCHAR* argv[])
{
    int v[] = { 1, 3, 3, 9, 7, 9, 4, 2, 8, 10 };
    //建立一个最大堆
    Heap<int> maxHeap(v, 10);
    //建立一个最小堆
    Heap<int, greater<int>> minHeap(v, 10);
    int k = 0;
    int size = maxHeap.Size();

    //堆排序
    for (int i = 0; i < size;++i)
    {
        if (maxHeap.GetTop(k))
            cout << k << " ";
        maxHeap.RemoveTop();
    }
    cout << endl;

    for (int i = 0; i < size; ++i)
    {
        if (minHeap.GetTop(k))
            cout << k << " ";
        minHeap.RemoveTop();
    }
    cout << endl;

    int v1[] = { 11, 33,19, 17, 14, 12, 18, 20 };
    for (int i = 0; i < 8;++i)
    {
        maxHeap.Insert(v1[i]);
    }
    Heap<int> He1(maxHeap);
    Heap<int> He2 = He1;
    size = He2.Size();
    for (int i = 0; i < size; ++i)
    {
        if (He2.GetTop(k))
            cout << k << " ";
        He2.RemoveTop();
    }
    cout << endl;

    return 0;
}

测试程序结果:
《每日一题24:堆》

点赞