每日一題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;
}

測試程序結果:
這裏寫圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章