本文記錄了使用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;
}
測試程序結果: