一:堆

堆数据结构是一种数组结构可以看做是一个完全二叉树;
堆的数据存储分为二种
大堆:每个父亲节点都大于孩子节点

小堆:每个父亲节点都小于汉子节点



接下来就是建堆,建堆实际就是讲一个二叉树调整成我们所需要的结果。
方法:从第一个非叶节点开始,找到它的最大值(大堆调整)或者是最小值(小堆调整),然后再与父节点比较;
我们通过仿函数,来选择我们需要的是大堆还是小堆。
template<class T>
class Less
{
public:
    bool operator()(const T&a, const T&b)
    {
        return a < b;
    }
};
template<class T>
class Greater
{
public:
    bool operator()(const T&a, const T&b)
    {
        return a>b;
    }
};

二:建堆:

调整分三步:
1:选出第一个非叶节点,选左孩子和右孩子的最大值,这里我们以大堆为例;
2:比较选出的孩子的大小与父亲比较,再交换,最后依次调整父亲和孩子的位置;
3:每个父亲节点都大于孩子节点则不用调整;
void _AjustDown(int root)
    {
        Compare comFuc;
        int parent = root;
        int child = parent * 2 + 1;//左孩子

        while (child < _a.size())
        {
            //选较大的孩子
            if (child + 1 < _a.size() && comFuc(_a[child + 1], _a[child]))
            {
                ++child;
            }
            //比较父亲与孩子的大小
            if (comFuc(_a[child], _a[parent]))
            {
                swap(_a[child], _a[parent]);
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break;
            }
        }
    }
建堆的时间复杂度是O( N*logN);

三:堆的插入:

前面我们知道堆可以抽象为完全二叉树,堆的插入主要影响当前节点到根节点的路径。所以只需对路径调整即可;
堆也是一种数组,对数组的插入可以参考vector的实现;
这里我们用向上调整算法,比较插入节点与父亲节点的大小,选择大的作为当前节点再与父亲节点比较,直到根节点;

//插入
void Push(const T&x)
{
	_a.push_back(x);
	_AjustUp(_a.size()-1);
}
//向上调整
void _AjustUp(int child)
    {
        assert(!_a.empty());
        int parent = (child - 1) >> 1;

        while (child>0)
        {
            //如果孩子节点的值大于父节点的值
            Compare comFunc;
            if (_a[child] > _a[parent])
            {
                swap(_a[child], _a[parent]);
                child = parent;
                parent = (child - 1) >> 1;
            }
            else
            {
                break;
            }
        }
    }

插入算法的时间复杂度是O(logN )

四:堆的删除:

如果直接尾删,堆的顺序就会被打乱不能保证它还继续为一个小堆,那怎么解决呢?
其实就是把根节点和最后一个节点交换,再尾删这样就保证堆的结构不会被破坏。

void Pop()
    {
        assert(!_a.empty());

            swap(_a[0], _a[_a.size() - 1]);//根节点和最后一个叶子节点交换
            _a.pop_back();
            _AjustDown(0);
    }
删除的复杂度O(1);

五:测试和实现:

#pragma once
#include<iostream>
#include<vector>
#include<assert.h>

using namespace std;
template<class T>
class Less
{
public:
    bool operator()(const T&a, const T&b)
    {
        return a < b;
    }
};
template<class T>
class Greater
{
public:
    bool operator()(const T&a, const T&b)
    {
        return a>b;
    }
};
//大堆:任意一个节点是它子树的最大节点
template<class T,class Compare = Greater<T>>
class Heap
{
public:
    Heap()
    {}
    Heap(T*a ,size_t n)
    {
        _a.reserve(n);//增容(可以直接拷贝数据)
        for (size_t i = 0; i < n; i++)
        {
            _a.push_back(a[i]);
        }
        //调整成堆
        for (int j =(_a.size()-2)/2; j >= 0; --j)
        {
            //向下调整
            _AjustDown(j);
        }
    }
    //插入
    void Push(const T&x)
    {
        _a.push_back(x);
        _AjustUp(_a.size()-1);
    }
    //删除
    void Pop()
    {
        assert(!_a.empty());

            swap(_a[0], _a[_a.size() - 1]);//根节点和最后一个叶子节点交换
            _a.pop_back();
            _AjustDown(0);

    }
    const T& Top()
    {
        assert(!_a.empty());
        return _a[0];
    }
    size_t Size()
    {
        return _a.size();
    }
    bool Empty()
    {
        return _a.empty();
    }

protected:
    //插入算法,主要影响root路径,向上
    void _AjustUp(int child)
    {
        assert(!_a.empty());
        int parent = (child - 1) >> 1;

        while (child>0)
        {
            //如果孩子节点的值大于父节点的值
            Compare comFunc;
            if (_a[child] > _a[parent])
            {
                swap(_a[child], _a[parent]);
                child = parent;
                parent = (child - 1) >> 1;
            }
            else
            {
                break;
            }
        }
    }
protected:
    //向下调整成堆
    void _AjustDown(int root)
    {
        Compare comFuc;
        int parent = root;
        int child = parent * 2 + 1;//左孩子

        while (child < _a.size())
        {
            //选较大的孩子
            if (child + 1 < _a.size() && comFuc(_a[child + 1], _a[child]))
            {
                ++child;
            }
            //比较父亲与孩子的大小
            if (comFuc(_a[child], _a[parent]))
            {
                swap(_a[child], _a[parent]);
                parent = child;
                child = parent * 2 + 1;
            }
            else
            {
                break;
            }
        }
    }
protected:
  vector<T>_a;
};
void TestHeap()
{
    int a1[] = {10, 16, 18, 12, 13, 15, 17, 14, 19 };
    size_t len = sizeof(a1) / sizeof(a1[0]);
    Heap<int,Greater<int>>  h1(a1,len);
    h1.Top();
    h1.Push(25);
    h1.Pop();
    while (!h1.Empty())
    {
        cout << h1.Top() << " ";
        h1.Pop();
    }
    cout << endl;
}





  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值