数据结构笔记-二叉堆[转]

转自(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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值