数据结构之堆

堆以及堆排序

堆的定义:
1.是一颗完全二叉树(除最后一层叶子节点,其他层级节点不为空,并且叶子节点全部要偏向左边)
2.当前节点的值要大于等于(小于等于)左右子树,那么就会有大顶堆和小顶堆的区别,大顶堆意味着堆顶值最大,小顶堆意味着堆顶最小

如何实现堆

由于完全二叉树的定义,那么堆完全可以使用数组的形式进行实现,通过下标来确定每个节点的位置,这样就省去了左右节点指针的额外开销。
1.插入:
方式1:从下往上进行插入,每次都将值放在最末端,通过不断与其父节点比较并且置换。
方式2:将所有数据先放入到数组中,由于最后一排叶子节点不需要进行堆化,那么我们可以直接从n/2节点开始进行堆化,直到堆顶(后面代码有说明)

实现堆以及堆排序

#include<stdlib.h>
#include<stdio.h>
#include<iostream>
#define MAX_CAPACITY 10
template<typename T>
class Heap
{
    Heap():m_data(new T[MAX_CAPACITY+1])
    {};

    ~Heap()
    {
        delete[] m_data;
    };

    //插入数据
    void insertData(T data)
    {
        if (m_curCount >= MAX_CAPACITY)return;
        m_curCount++;
        m_data[m_curCount] = data;
        int nSize = m_curCount;
        while(nSize / 2 > 0 && m_data[nSize] >= m_data[nSize / 2])
        {
            std::swap(m_data,nSize,nSize/2);
            nSize /= 2;
        }
    };

    //删除操作
    void deleteData()
    {
        if (m_curCount == 0)return;
        std::swap(m_data,m_curCount,1);
        m_curCount--;
        int nMax = 1;
        while(true)
        {
            int nMaxIndex = nMax;
            if (2 * nMax <= m_curCount && m_data[2*nMax] >= m_data[nMax])
                nMaxIndex = 2 * nMax;
            if( 2 * nMax + 1 <= m_curCount && m_data[2 * nMax + 1] >= m_data[nMaxIndex])
                nMaxIndex = 2 * nMax + 1; 
            if(nMax == nMaxIndex) break;
            //每一次都更新下值的位置
            std::swap(m_data,nMax,nMaxIndex);
            nMax = nMaxIndex;
        }
    };

    void buildHeap(T* data,int n)
    {
        for(int i = n / 2; i > 0; i--)
        {
            heapify(data,n,i);
        }
    };

    void heapify(T* data,int n,int i)
    {
        while(true)
        {
            int nMaxIndex = i;
            if(2*i <= n && m_data[2*i] >= m_data[i])
                nMaxIndex = 2*i;
            if (2*i+1 <= n && m_data[2*i+1] >= m_data[nMaxIndex])
                nMaxIndex = 2*i+1;
            if(nMaxIndex == i)break;
            std::swap(m_data,nMaxIndex,i);
            i = nMaxIndex;
        }
    };

    void sort(T* data,int n)
    {
        //先建堆
        buildHeap(data,n);
        //删除堆顶数据
        int size = n;
        while(size > 1)
        {
            //每次弹出堆顶,然后进行处理
            std::swap(data,size,1);
            size--;
            heapify(data,size,1);
        }
    }

private:
    T* m_data;
    int m_curCount;
};

上面代码中,从上往下进行堆化,只需要处理n/2个节点之前的数据就行了,因为叶子节点没有左右子节点,所以不需要进行堆化,而应该从倒数第二排进行堆化,假设一共有n层,那么第一层为1个节点,第二层为2个节点,依次类推,第n层为(2……n-1),第n-1层为(2……n-2),那么此时除开最后一层,前面的层数节点之和为:1+2+4+…(2……n-2)=(2……n-1)-1,比最后一层的数据少一个,那么n个数据,其中n/2+1到n的节点一定都是叶子节点

堆排序的性能分析

由于建堆的过程不需要涉及到所有节点,只需要涉及到n/2 个节点,因为堆化的开销与高度相关,所以令高度为h,那么总时间开销为:S1 = 1h+2(h-1)+…(2……n-1)*1,通过错位相乘再相减,2S1-S1 = (2……h-1)-h-2,因为h的大致时间为logn,所以时间复杂度近似为O(n),而排序就是一个删除过程,每个节点删除之后都需要重新建堆,时间复杂度为O(nlogn),所以总的时间复杂度为O(nlogn)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值