Heap【堆】

1.堆得介绍

堆得数据结构是一种数组对象,堆可以被看做一颗完全二叉树(借助二叉树的思想建堆以及插入和删除比较形象直观)

2.堆得分类:

①最大堆:每个父节点>它的孩子结点
②最小堆:每个父节点<它的孩子结点

3.堆得应用

①优先级队列
①堆排序
4.堆得底层是一个数组,学了STL库之后,可以将底层写层vector,可以动态增容

5.堆得创建

将一个数组中的元素进行向下调整,调成大堆或者小堆

6.时间复杂度:

建堆O(N*lgN); 插入O(lgN); 删除O(lgN)

实现:将一个数组中的的元素进行向下调整,就可以建成大堆或者小堆;然后如果要给建好的大堆或者小堆中插入元素,
则是把这个元素插到堆得尾端,然后从插入的尾端地方开始进行向上调整;如果是要删除堆顶的元素,则是要先把堆顶
的元素和堆尾的元素进行交换,然后删除堆尾的元素,然后从堆顶开始进行向下调整;重新或者一个大堆或者小堆;
有时候,我们既需要大堆,也需要小堆,当然可以实现两个类,大堆类和小堆类。但是这样代码的重复性太强;
仔细想了之后,我们发现大堆和小堆最大的区别就在于个结点与孩子结点的大小关系,其他的思路什么的都是一样的,两个类就达不到代码的复用性。
所以我们可以采用仿函数,又叫函数对象,通过它来实现代码复用。

这里写图片描述
这里写图片描述
这里写图片描述

7.代码实现

#include<iostream>
using namespace std;
#include<vector>
#include<cassert>

template<typename T>
struct Great//大堆
{
    bool operator()(const T& left,const T& right)
    {
        return left>right;
    }
};
template<typename T>//小堆
struct Small
{
    bool operator()(const T& left,const T& right)
    {
        return left<right;
    }
};
template<typename T,typename Compare=Great<T>>
class Heap
{
public:
    Heap(const T* arr=NULL,int sz=0)//构造
    {
        //1.先将数组中的元素全部放入堆中
        for (int i=0;i<sz;i++)
        {
            _heap.push_back(arr[i]);
        }
        //2.调整堆--建大堆或者小堆
        for(int i=(sz-2)/2;i>=0;i--)
        {
            AdjustDown(i);//向下调整                                                                                                                                                                                                    
        }
    }
    void Push(const T& x)//给堆中插入元素
    {
        //1.先将该元素插入堆中
        _heap.push_back(x);
        //2.然后向上调整
        AdjustUp(_heap.size()-1);
    }
    void Pop()//从堆中删除元素--大堆删除最大/小堆删除最小的元素
    {
        assert(_heap.size()>0);
       //1.将尾元素放入第一个位置
        _heap[0]=_heap[_heap.size()-1];
        _heap.pop_back();//删掉尾元素
       //2.从堆顶端向下调整
        AdjustDown(0);
    }
    const T& Top()//返回堆顶的元素--最大值或者最小值
    {
         return _heap[0];
    }
    size_t Size()//返回堆中的元素个数
    {
         return  _heap.size();
    }
    void Printf()//打印堆
    {
          for (size_t i=0;i<_heap.size();i++)
          {
               cout<<_heap[i]<<" ";
          }
          cout<<endl;
    }
private:
    //从堆的根结点开始向下调整
    void AdjustDown(int father)//向下调整
    {
        Compare com;
        size_t child=2*father+1;
        while (child< _heap.size())
        {

            //寻找该节点的左右孩子中的最大值的下标
            if (child+1<_heap.size()&&com(_heap[child+1],_heap[child]))
            {
                child++;
            }
            //比较左右孩子的最大值和父节点的大小,如果父节点小于,则进行交换,要么调整结束
            if( child < _heap.size()&&com(_heap[child],_heap[father]))
            {
                swap(_heap[father],_heap[child]);
                father=child;
                child=2*father+1;
            }
            else
                //当已经调整到堆底或者根结点不小于左右孩子的最大值,调整结束
                break;
        }
    }
    //从堆得最后一个结点开始向上调整
    void AdjustUp(int child)//向上调整
    {
         Compare com;
         int father=(child-1)/2;
         while (child>0)
         {
             if (com(_heap[child],_heap[father]))
             {
                 swap(_heap[child],_heap[father]);
                 child=father;
                 father=(child-1)/2;
             }
             else
                 break;
         }
    }
private:
    vector<T> _heap;
};
int main()
{
     int a [] = {10,11, 13, 12, 16, 18, 15, 17, 14, 19};
     size_t sz=sizeof(a)/sizeof(a[0]);
     Heap<int,Small<int>> hp(a,sz);
     cout<<"原始堆:"<<endl;
     hp.Printf();
     cout<<"最值:"<<hp.Top()<<endl;
     cout<<"堆元素个数:"<<hp.Size()<<endl;

     //hp.Push(2);
     //cout<<"向堆中插入2:"<<endl;
     //hp.Printf();
     //cout<<"最值:"<<hp.Top()<<endl;
     //cout<<"堆元素个数:"<<hp.Size()<<endl;


     cout<<"删除堆得最小值:"<<endl;
     hp.Pop();
     hp.Printf();
     cout<<"最值:"<<hp.Top()<<endl;
     cout<<"堆元素个数:"<<hp.Size()<<endl;

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值