堆实现及堆的基本接口

堆的实质是一个完全二叉树并具有以下属性:每一个子树的父节点都大于或等于左右孩子节点,这种堆称为大堆;每一个子树的父节点都小于或等于左右孩子节点,这种堆称为小堆。

由于堆的父节点和子节点之间有清楚地表示方法,所以堆可以不采用链式存储,这时堆是用顺序结构进行存储的,用STL中的vector存储数据。

设父节点的编号是m,则左孩子节点的编号是m*2+1,右孩子节点的编号是m*2+2;
设子节点的编号是i,则父节点的编号是(i-1)/2 .

为了实现大堆和小堆,必须依赖于两个算法:向上调整算法,向下调整算法

首先定义仿函数,用仿函数实现两个节点数值的比较。

template <class T>
struct Less
{
    bool operator()(const T& l, const T& r)
    {
        return l < r;
    }
};
template<class T>
struct Greator
{
    bool operator()(const T& l, const T& r)
    {
        return l>r;
    }
};

向上调整算法:

当一个已经调整好的堆,在堆尾又插入了一个数之后,这时堆的顺序被打乱,利用向上调整算法以新插入的节点和父节点为一条路径进行向上调整。
这里写图片描述

void AdjustUp(int child)
    {
        Compare ref;
        int parent = (child - 1) / 2;
        while (child > 0)
        {
            if (ref( _a[parent],_a[child]))
            {
            //顺着父节点和插入节点的路径向上调整
                swap(_a[child], _a[parent]);
                child = parent;
                parent = (child - 1) / 2;
            }
            else
            {
                break;
            }
        }
    }

向下调整算法:

向下调整算法采用子树的思想,将每一个节点看作一个子树进行向下调整。
具体思路是:先找出最后一个叶子节点的父节点,在子树中选出大的或小的子树和父节点进行比较,如果父节点小于或大于选出的子节点就将父节点和子节点进行交换,然后在判断子节点有没有子树再进行调整;当这个子树调整完之后,父节点进行减减再去调整下一个子树。

void AdjustDown(int parent)
    {
        Compare ptr;
        int child = parent * 2 + 1;
        while (child <(int) _a.size())
        {
            if (child + 1 < (int)_a.size() && ptr(_a[child], _a[child+1]))
            {
                ++child;
            }
            if (ptr(_a[parent], _a[child]))
            {
            //交换并判断有没有子树
                swap(_a[child], _a[parent]);
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break;
            }
        }
    }

堆的构造函数:

vector中扩容函数reserve和resize的区别:

resize():重新申请并改变当前vector对象的有效空间大小,并进行初始化;
reserve():重新申请并改变当前vector对象的总空间(_capacity)大小。

template<class T,class Compare>
class Heap
{
public:
    //这里堆的默认构造函数和有参数的构造函数都需要写
    //因为没有缺省构造函数的类成员变量需要在初始化列表初始化
    Heap()
    {}
    Heap(T* a, size_t size)
    {
        //对vector进行容量扩充
        _a.reserve(size);
        //将数组中的元素压入vector中
        for (int i = 0; i < (int)size; ++i)
        {
            _a.push_back(a[i]);
        }
        //建堆
        //(_a.size-2)/2代表第一个叶子节点的父亲节点
        for (int i = (_a.size() - 2) / 2; i >= 0; --i)
        {
            //向下调整算法
            AdjustDown(i);
        }
    }

堆的插入:

void Push(const T& x)
    {
        _a.push_back(x);
        AdjustUp(_a.size() - 1);
    }

堆的删除:

思路:要删除堆顶元素,直接删的话效率低,所以将堆顶元素和堆底元素进行交换,将堆底元素进行pop_back操作,再对堆内剩下的节点从父节点进行向下调整。

void Pop()
    {
        assert(!_a.empty());
        swap(_a[0], _a[_a.size() - 1]);
        _a.pop_back();
        AdjustDown(0);
    }

取堆顶元素:

const T& Top()
    {
        if (_a.size() == 0)
        {
            return NULL;
        }
        return _a[0];
    }
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值