一、堆的初始化
1、堆的概念
堆实际上是一种完全二叉树,堆可分为大堆和小堆。大堆就是左右孩子的值都小于其父节点的值,根节点的值最大;小堆就是左右孩子的值都小于其父节点的值,根节点的值最小。
2、建堆
第一步:先给出一个数组
这里写图片描述

第二步:根据该数组,将其构建成完全二叉树的结构
这里写图片描述
第三步:以这个完全二叉树为基础构造初始堆,从最后一个非叶子节点开始向上调整,这里以小堆为例
这里写图片描述
这里对以上调整过程加以解释说明:
(1)调整的时候从最后一个非叶子节点向上调整,设最后一个非叶子节点的位置为index,数组的大小为size,则index=size/2-1。
如上图:最后一个非叶子节点的位置为4,数组的大小为10,则4=10/2-1。
(2)向最后一个非叶子节点开始调整的过程是:
先比较左右孩子的大小,找出左右孩子中较小的那一个;
然后将左右孩子中较小的那一个与其父节点进行比较,如果父节点较大就进行交换,否则就去调整下一棵子树。
第四步:代码实现

template <class T>
class Heap
{
public:
    //缺省的构造函数
    Heap()
    {}

    Heap( T* a, size_t size)
    {
        //将数组中的数据放到顺序表里面
        for (size_t i = 0; i < size; i++)
        {
            _a.reserve(size);
            _a.push_back(a[i]);
        }
        //进行调整
        for (int i = _a.size() / 2 - 1; i >= 0; --i)
        {
            _AdjustDown(i)
        }

    }
    protected:
    void _AdjustDown(int root)
    {
        int left = root * 2 + 1;
        int right = left + 1;
        //如果左孩子不存在,则root为叶子节点,已不需再调整
        while (left < _a.size())
        {
            //找出左右孩子中较小的那一个
            int min = left;
            //注意判断右孩子有没有越界
            if (right<_a.size() &&
             _a[left]>_a[right])
            {
                min = right;
            }
            //找出左右孩子中较小的那一个与父节点进行比较
            if (_a[min] < _a[root])
            {
                swap(_a[min], _a[root]);
                root = min;
                left = root * 2 + 1;
                right = left + 1;
            }
            else
                break;
        }
    }
    private:
    vector<T> _a;
};

二、相关操作
1、向堆中插入元素,并保证堆的性质
(1)、步骤
步骤一:向存储堆的顺序表中push进去一个数据x;
步骤二:从堆的最后一个叶子节点开始向上调整
(2)、举例说明
这里写图片描述
(3)、代码实现

//向堆尾插入元素
    void Push(int x)
    {
        _a.push_back(x);
        _AdjustUp(_a.size() - 1);
    }

//从最后一个叶子节点开始往上调整
    void _AdjustUp(int root)
    {
        int child=root;
        while (child > 0)
        {
            int parent = (child - 1) / 2;
            if (_a[child] < _a[parent])
            {
                swap(_a[child], _a[parent]);
                child = parent;
            }
            else
            {
                break;
            }
        }
    }

2、删除堆顶元素
(1)、步骤
步骤一:将堆顶元素和最后一个非叶子节点进行交换;
步骤二:将交换后的最后一个非叶子节点删除;
步骤三:从堆顶开始往下进行调整
(2)、举例说明
这里写图片描述
(3)、代码实现

    //删除堆顶元素
    void Pop()
    {
        swap(_a[0], _a[_a.size() - 1]);
        _a.pop_back();

        _AdjustDown(0);
    }

三、堆排序
1、算法描述
堆排序实际上是一种选择排序,如果想要降序排列,则先构建一个小堆,设小堆的大小为size
步骤一:先将堆顶元素和最后一个元素进行交换;
步骤二:将size减1,缩小堆的大小,即不包括最后一个元素;
步骤三:将size缩减后再进行调整,即除过最后一个元素之外,将剩下的元素调整成小堆结构。
2、举例说明
这里写图片描述
3、代码实现

void HeapSort(int n)
    {
        //先调成小堆结构
        for (int i = _a.size() / 2 - 1; i >= 0; --i)
        {
            _AdjustDownSort(i, n);
        }

        int end = n - 1;
        while (end)
        {
            swap(_a[0], _a[end]);
            _AdjustDownSort(0, end);
            --end;
        }
    }

//从最后一个非叶子节点开始调整(排序用)
    void _AdjustDownSort(int root,int n)
    {
        int left = root * 2 + 1;
        int right = left + 1;
        //如果左孩子不存在,则root为叶子节点,已不需再调整
        while (left < n)
        {
            //找出左右孩子中较小的那一个
            int min = left;
            //注意判断右孩子有没有越界
            if (right<n && _a[left]>_a[right])
            {
                min = right;
            }
            //找出左右孩子中较小的那一个与父节点进行比较
            if (_a[min] < _a[root])
            {
                swap(_a[min], _a[root]);
                root = min;
                left = root * 2 + 1;
                right = left + 1;
            }
            else
                break;
        }
    }

说明:由于每一调整的范围不同,因此_AdjustDownSort()函数需要两个形参,所以该函数与上面的调整函数不太相同。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值