转自(http://www.cppblog.com/guogangj/archive/2012/04/23/99729.html)
二叉堆(Binary Heap)
首先说说数据结构概念——堆(Heap),其实也没什么大不了,简单地说就是一种有序队列而已,普通的队列是先入先出,而二叉堆是:最小先出。
这不是很简单么?如果这个队列是用数组实现的话那用打擂台的方式从头到尾找一遍,把最小的拿出来不就行了?行啊,可是出队的操作是很频繁的,而每次都得打一遍擂台,那就低效了,打擂台的时间复杂度为Ο(n),那如何不用从头到尾fetch一遍就出队呢?二叉堆能比较好地解决这个问题,不过之前先介绍一些概念。
完全树(Complete Tree):从下图中看出,在第n层深度被填满之前,不会开始填第n+1层深度,还有一定是从左往右填满。
再来一棵完全三叉树:
这样有什么好处呢?好处就是能方便地把指针省略掉,用一个简单的数组来表示一棵树,如图:
那么下面介绍二叉堆:二叉堆是一种完全二叉树,其任意子树的左右节点(如果有的话)的键值一定比根节点大,上图其实就是一个二叉堆。
二叉堆可以分为最小堆和最大堆:
最小堆:根结点的键值是所有堆结点键值中最小者的堆
最大堆:根结点的键值是所有堆结点键值中最大者的堆。
下面以最小堆为例,对二叉堆的操作进行分析:
你一定发觉了,最小的一个元素就是数组第一个元素,那么二叉堆这种有序队列如何入队呢?看图:
假设要在这个二叉堆里入队一个单元,键值为2,那只需在数组末尾加入这个元素,然后尽可能把这个元素往上挪,直到挪不动,经过了这种复杂度为Ο(logn)的操作,二叉堆还是二叉堆。
那如何出队呢?也不难,看图:
出队时将最小值(对顶)值取出,让后将堆底的值移到堆顶。再根据最小堆的规则整理二叉堆就行了。这种操作的复杂度也是Ο(logn),比Ο(n)强多了吧?
下面是代码:
MinHeap.h
template<class T>
class CMinHeap
{
public:
CMinHeap(); // With default max size
CMinHeap(int nMaxSize); // With manual max size
virtual ~CMinHeap();
public:
// Insert function
bool Insert(const T &Value);
// Minimum value operation
bool RemoveMin( T& MinValue );
bool ReplaceMin( T& Value );
bool GetMin( T& MinValue );
// Heap state check function
bool IsEmpty();
bool IsFull();
void Clear();
// Test Function
#ifdef _DEBUG
bool GetValue( T& Value, const nPos );
#endif
private:
void FilterDown(const int nPos );
void FilterUp(const int nPos);
private:
T* m_pHeap;
int m_nMaxSize;
int m_nHeapEnd;
};
MinHeap.cpp
template<class T>
CMinHeap<T>::CMinHeap()
{
m_nMaxSize = 1000;
m_pHeap = new T[m_nMaxSize];
m_nHeapEnd = -1;
}
template<class T>
CMinHeap<T>::CMinHeap( int nSize )
{
m_nMaxSize = nSize;
m_pHeap = new T[m_nMaxSize];
m_nHeapEnd = -1;
}
template<class T>
CMinHeap<T>::~CMinHeap()
{
delete[] m_pHeap;
m_pHeap = NULL;
}
template<class T>
bool CMinHeap<T>::Insert( const T &Value )
{
if(m_nHeapEnd == (m_nMaxSize - 1))
{
return false;
}
m_nHeapEnd++;
m_pHeap[m_nHeapEnd] = Value;
FilterUp(m_nHeapEnd);
return true;
}
template<class T>
bool CMinHeap<T>::RemoveMin( T& MinValue )
{
if ( m_nHeapEnd < 0 )
{
return false;
}
MinValue = m_pHeap[0];
m_pHeap[0] = m_pHeap[m_nHeapEnd];
m_nHeapEnd--;
FilterDown( 0 );
return true;
}
template<class T>
bool CMinHeap<T>::ReplaceMin( T& Value )
{
if ( m_nHeapEnd < 0 )
{
return false;
}
T NewValue = Value;
Value = m_pHeap[0];
m_pHeap[0] = NewValue;
FilterDown( 0 );
return true;
}
template<class T>
bool CMinHeap<T>::GetMin( T& MinValue )
{
if ( m_nHeapEnd < 0)
{
return false;
}
MinValue = m_pHeap[0];
return true;
}
template<class T>
void CMinHeap<T>::FilterDown( const int nPos )
{
int i = nPos, j = 2 * i + 1;
T TempValue = m_pHeap[i];
while( j <= m_nHeapEnd )
{
if( j < m_nHeapEnd &&
m_pHeap[j] > m_pHeap[j+1] )
{
j++;
}
if(TempValue <= m_pHeap[j])
{
break;
}
else
{
m_pHeap[i] = m_pHeap[j];
i = j;
j = 2*j + 1;
}
}
m_pHeap[i] = TempValue;
}
template<class T>
void CMinHeap<T>::FilterUp( const int nPos )
{
int j = nPos, i = (j-1)/2;
T TempValue = m_pHeap[j];
while( j>0 )
{
if(m_pHeap[i] <= TempValue)
{
break;
}
else
{
m_pHeap[j] = m_pHeap[i];
j = i;
i = (i-1)/2;
}
}
m_pHeap[j] = TempValue;
}
template<class T>
void CMinHeap<T>::Clear()
{
m_nHeapEnd = -1;
}
#ifdef _DEBUG
template<class T>
bool CMinHeap<T>::GetValue( T& Value, const nPos )
{
if ( nPos > m_nHeapEnd )
{
return false;
}
Value = m_pHeap[nPos];
return true;
}
#endif