poppo大根堆的原理与实现。

大根堆的定义:1 大根堆是一个大根树 2 大根堆是一个完全二叉树

所以大根堆用数组表示是连续的,不会出现空白字段。

对于大根堆的插入

对于大根堆的插入,可以在排序前确定大根堆的形状,可以确定元素5从位置6插入,那么比较元素5和位置3的元素2,

元素5比元素2大,将2下移。接着比较元素5和元素20,一次类推,直到找到元素5的合理位置。

接着看一下如果插入的元素是21,怎么进行排序。

21比2大,所以将2下移,接着比较21和20,发现20比21小,20下移,最终21放到

根的位置。形成大根堆。

对于大根堆的删除

大根堆删除根元素,那么可以确定删除后的形状。可以理解成将最后一个叶子节点放在

合理位置,首先比较叶子节点元素10和根节点的两个孩子15和2,选出两个节点中最大的

元素15,15比10大,所以15进行气泡。放到根节点。然后15所在的位置2,变为不确定的问号。

由于14比10大,那么14起泡放到位置2,根据大根堆的形状,最后将10放到左节点。

将一个无序的完全二叉树变为大根堆

将一个无序的完全二叉树变为大根堆(或者小根堆),首先要找到最有一个叶子节点的父节点,

对该父节点为根节点的子树进行排序,生成一个大根堆(小根堆)。然后从节点位置依次

向前做同样的排序,将该节点到根节点的所有子树变为大根堆(小根堆)

举例子:

如上图所示,因为总共有6个节点,6/2 = 3,所以元素19的父节点是位置3的元素4,

将以4位根的子树变为大根堆。因为19比4大,所以19上移,4做叶子节点。依次类推,

从位置3到位置1的所有子树都按照这种逻辑处理,最终变成大根堆。

接着要处理位置2的子树,位置2的元素为1,两个节点为25和12,选最大的元素25,因为

25比1大,所以25进行上移,1变为叶子节点。这样位置2的子树就处理完了。

接着处理位置1,因为位置1的元素为6,两个节点分别为25和19,取最大节点元素25,

因为25比6大,所以25上移,而此时位置2还有两个节点元素1和元素12,需要比较元素6

和这两个节点中最大的,以确定大根堆。由于12比6大,所以12上移,6变为叶子节点。

最终用数组表示这个大根堆就是[25,12,19,1,6,4]

下面是代码实现和测试:

大根堆的类结构:

template <class T>
class maxHeap
{
public:
    maxHeap(void)
    {
        m_nHeapSize = 0;
            m_nHeapCapacity = 0;
            m_pHeapArray = NULL;
    }

    maxHeap(const maxHeap& tempHeap);
    maxHeap(T * heapArray, int arrayLen);

    ~maxHeap(){

        if(m_pHeapArray)
        {
            free(m_pHeapArray);
        }

        m_pHeapArray = NULL;
        m_nHeapSize = 0;
        m_nHeapCapacity = 0;
    }

    //插入节点
    void insertNode(const T& t);
   //pop堆顶元素
    const T& popRoot();
    //打印自己的堆元素,用数组表示法输出
    void printHeap();
    //将一个无序的数组变为大根堆
    void createMaxHeap(T * heapArray, int arrayLen);
    //销毁自己的堆元素
    void deallocMaxHeap();
    //打印数组的元素
    void printHeap(T * heapArray, int arrayLen);

private:
    //堆的数组元素,连续区间首地址
    T*  m_pHeapArray;
    //当前使用的大小
    int m_nHeapSize;
    //堆的容量,实际开辟的大小
    int m_nHeapCapacity;
};

两个构造函数:

template <class T>
maxHeap<T>::maxHeap(const maxHeap  &tempHeap){
    m_nHeapSize = tempHeap.m_nHeapSize;
    m_pHeapArray = malloc(sizeof(class maxHeap) *m_nHeapSize);
    m_nHeapCapacity = m_nHeapSize;
}

template <class T>
maxHeap<T>::maxHeap(T * heapArray, int arrayLen)
{
    m_nHeapSize = arrayLen;
    m_pHeapArray = malloc(sizeof(class maxHeap) * m_nHeapSize);
    m_nHeapCapacity = arrayLen;
}

插入节点:

template <class T>
void maxHeap<T>::insertNode(const T& node)
{
    m_nHeapSize ++;
    if(m_nHeapSize >= m_nHeapCapacity)
    {
        m_pHeapArray = (T *)realloc(m_pHeapArray, sizeof(T) * m_nHeapSize *2);
    }

    m_nHeapCapacity = m_nHeapSize*2;

    //当前节点所在位置
    int currentIndex = m_nHeapSize;
    //该节点父节点所在位置
    int parentIndex = currentIndex/2;
    //当前节点为根节点,跳出循环直接插入即可
    while(currentIndex != 1)
    {
        //父节点元素小于该node,因为是大根堆,所以父节点下移
        if(m_pHeapArray[parentIndex -1] < node)
        {
            //父节点数据下移
            m_pHeapArray[currentIndex - 1] = m_pHeapArray[parentIndex -1];
            //更新当前节点位置,当前比较位置上移
            currentIndex = currentIndex/2;
            //父节点位置同样上移
            parentIndex =  parentIndex/2;
        }
        else
        {
            break;
        }
    }
    //因为节点数是从1开始的,所以节点数-1表示数组中的位置
    m_pHeapArray[currentIndex -1] = node;

}

打印元素:

template <class T>
void maxHeap<T>::printHeap()
{
    cout <<"current max heap array is :" << endl;
    for(int i = 0; i < m_nHeapSize; i++)
    {
        cout << m_pHeapArray[i] << " ";
    }
    cout << endl;
}

template <class T>
void maxHeap<T>::printHeap(T * heapArray, int arrayLen)
{
    cout <<"current max heap array is :" << endl;
    for(int i = 0; i < arrayLen; i++)
    {
        cout << heapArray[i] << " ";
    }
    cout << endl;
}

pop堆顶的元素,取出最大值

template <class T>
const T& maxHeap<T>::popRoot()
{
    //先取出最后的叶子节点
    const T& lastEle = m_pHeapArray[m_nHeapSize-1];

    //更新heapsize
    m_nHeapSize --;

    //删除时需要从根节点开始,找到最大值起泡
    int currentIndex= 1;
    //当前节点的做孩子
    int leftChild = currentIndex *2;
    //当前节点的孩子节点超过堆大小,说明该节点为叶子节点
    while(leftChild <= m_nHeapSize)
    {
        int bigChild = leftChild;
        //取出两个孩子中大的孩子,然后将大的孩子节点数据上移
        if(leftChild < m_nHeapSize && m_pHeapArray[leftChild-1] < m_pHeapArray[leftChild])
        {
            //更新大孩子节点为右节点
            bigChild = leftChild +1;
        }
        //比较两个节点中大的孩子节点和取出的最后叶子节点,那个数值大
        //如果最后的叶子节点数值大,那么可以跳出循环,因为找到了lastEle的合理位置
        //剩余的树也是大根堆
        if(m_pHeapArray[bigChild -1] <= lastEle)
        {
            break;
        }
        //大节点数据上移
        m_pHeapArray[currentIndex -1] = m_pHeapArray[bigChild-1];
        //更新插入位置为当前大节点位置
        currentIndex = bigChild;
        leftChild = currentIndex *2;
    }

    m_pHeapArray[currentIndex-1] = lastEle;

    return lastEle;
}

将一个无序的数组元素,变为大根堆

template <class T>
void maxHeap<T>::createMaxHeap(T * heapArray, int arrayLen)
{
    //判断异常
    if(arrayLen <= 0 || heapArray == NULL)
    {
        return ;
    }

    //从最后一个叶子节点的父节点开始,依次从该位置到根节点
    //例如该位置为3,那么位置3,位置2,位置1的根节点的子树依次处理为大根堆

    int currentIndex = arrayLen;
    //父节点位置
    int beginIndex =  currentIndex/2;
    //依次处理,形成子树大根堆
    for(int i = beginIndex; i > 0;  i--)
    {
        int rootEle = heapArray[i-1];

        int curNode = i;
        int leftChild = i *2;
        while(leftChild <= arrayLen)
        {
            int bigChild = leftChild;

            int rootElePrint =  heapArray[leftChild-1];
            int rightElePrint = heapArray[leftChild+1 -1] ;

            if(leftChild +1 <= arrayLen && heapArray[leftChild+1 -1] > heapArray[leftChild-1])
            {
                bigChild = leftChild +1;
            }

            if(heapArray[bigChild -1] <= rootEle )
            {
                break;
            }

            heapArray[curNode -1] = heapArray[bigChild -1];
            curNode = bigChild;
            leftChild = curNode *2;
        }

        heapArray[curNode -1] = rootEle;

  }
}

源代码下载地址: http://download.csdn.net/detail/secondtonone1/9575112

整个代码就到这里吧,这是我的公众号,希望关注下:

时间: 2024-10-25 06:47:03

poppo大根堆的原理与实现。的相关文章

大根堆

2017-07-24 22:04:08 writer:pprp 参考书目:张新华的<算法竞赛宝典> 思路跟小根堆一个样,主要的思路是先构造一个大根堆,然后在每次将最大的一个排除出来,再进行堆排序 代码如下: #include <iostream> using namespace std; const int maxn = 100; int a[maxn],n,heapsize; void maxheapify(int i) //根据数组的下表对应节点,对其左右两个子节点进行堆构造:

hdu 4857 逆拓扑+大根堆(priority_queue)

题意:排序输出:在先满足定约束条件下(如 3必需在1前面,7必需在4前面),在满足:1尽量前,其次考虑2,依次.....(即有次约束). 开始的时候,只用拓扑,然后每次在都可以选的时候,优先考虑小的,其实没什么简单,如 图(3-->1,2)这样输出是2.3.1,正确应该是 3 1 2,因为 1要尽量前(都满足第一约束). 参考他人思路结合自己理解:因为这样的弊端就是没有考虑这种情况:图中:若我的"子孙"中,有的比你次优先,虽然现在我们都可以输出,但是要考虑我的子代,若我的子代有次

大根堆(模板)

今天学了大根堆,第一次从头到尾个人手打,虽说有些STL能代替堆但效率很低,算了算300000的数据甚至要跑500ms.... 也算记录一下吧. 自己的:83ms(300000) %:pragma GCC optimize(3) #include<bits/stdc++.h> #define N 500010 #define INF 0x3f3f3f3f using namespace std; struct Heap { int heap[N]; int size; void init() {

Java实现堆排序(大根堆)

堆排序是一种树形选择排序方法,它的特点是:在排序的过程中,将array[0,...,n-1]看成是一颗完全二叉树的顺序存储结构,利用完全二叉树中双亲节点和孩子结点之间的内在关系,在当前无序区中选择关键字最大(最小)的元素. 1. 若array[0,...,n-1]表示一颗完全二叉树的顺序存储模式,则双亲节点指针和孩子结点指针之间的内在关系如下: 任意一节点指针 i:父节点:i==0 ? null : (i-1)/2  左孩子:2*i + 1  右孩子:2*i + 2 2. 堆的定义:n个关键字序

堆排序—大根堆,小根堆

1.小根堆 若根节点存在左子女则根节点的值小于左子女的值:若根节点存在右子女则根节点的值小于右子女的值. 2.大根堆 若根节点存在左子女则根节点的值大于左子女的值:若根节点存在右子女则根节点的值大于右子女的值. 3.结论 (1)堆是一棵完全二叉树(如果公有h层,那么1~h-1层均满,在h层连续缺失若干个右叶子). (2)小根堆的根节点的值是最小值,大根堆的根节点的值是最大值. (3)堆适合于采用顺序存储. 4.堆的插入算法 将一个数据元素插入到堆中,使之依然成为一个堆. 算法描述:先将结点插入到

bzoj4919: 大根堆

Description 给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点.每个点有一个权值v_i. 你需要将这棵树转化成一个大根堆.确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j. 请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树. Input 第一行包含一个正整数n(1<=n<=200000),表示节点的个数. 接下来n行,每行两个整数v_i,p_i(0<=v_i<=10^9

大根堆pop push详细注释

//大根堆procedure push(x:longint);//元素x入堆 O(log t)var  tx,i:longint;begin  inc(t);//堆顶top加1  a[t]:=x;//将x放入堆的最后一个节点  i:=t;  while (i>1)and(a[i>>1]<a[i]) do//将元素x一层一层向上传递直到 到达根或上一层大于本身<=>找到x应在的位置    begin    tx:=a[i>>1];     a[i>>

大根堆的创建过程

初始堆就是大根堆,只是是第一次(初始序列)调整,第一次必须是自底向上逐个调整,以后(第一次交换后)是自上向下调整(因为除了第一个即堆顶元素,其他都是已经调整好的堆).过程:先把数据画出一颗二叉树:                     40           30                92    16         20     47       25 56   55  35从最后一个数据的双亲(20)开始,数据最大的成为双亲20和35交换:下一个双亲(16),16和56交换:双

序列——堆排序-大根堆(堆大顶)

1.小根堆 如果根是儿童的存在留下的根值左孩子小于值:如果根是儿童的权利的存在的根值比他们的孩子的权利少值. 2.大根堆 如果根是儿童的存在留下的根值多名离开自己的孩子值.子女则根节点的值大于右子女的值. 3.结论 (1)堆是一棵全然二叉树(假设公有h层,那么1~h-1层均满,在h层连续缺失若干个右叶子). (2)小根堆的根节点的值是最小值,大根堆的根节点的值是最大值. (3)堆适合于採用顺序存储. 4.堆的插入算法 将一个数据元素插入到堆中,使之依旧成为一个堆. 算法描写叙述:先将结点插入到堆