堆及其应用

应用1.优先级队列

优先级队列 是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。

#pragma once
#include <cassert>
#include <iostream>
#include <cstdlib>
#include "Heap.h"
using namespace std;

template <class T , class Compare = Greater<T>>
class PriorityQueue
{
public:
    PriorityQueue(T* array, size_t size)
        :_h(array, size)
    {}
    void Push(const T& d)
    {
        _h.Push(d);
    }
    void Pop()
    {
        _h.Pop();
    }
    const T& Top()
    {
        return _h.GetTop();
    }
protected:
    Heap <T, Compare> _h;
};
void TestPriorityQueue()
{
    int a[] = { 3, 8, 12, 2, 19, 11, 14, 13, 15, 10 };
    PriorityQueue<int> a1(a, 10);
}

应用2.n个数中找出最大的前k个数

在有大量的数据时,找出最大的前k个数。此类问题可以用堆来解决。

首先,取出n个数据中的前k个数,创建一个有k个数据的小堆。
然后,把后面的数据依次和堆顶的数据比较,找出俩个数中大的数据放在堆顶,调用向下调整算法,保证这是一个小堆。这样就可以找出最大的前k个数。

找出最小的k个数的方法也类似。
注意:找最大数时建的是小堆,找最小数时建的是大堆。

#pragma once
#include <cassert>
#include <iostream>
#include <cstdlib>
using namespace std;
#define K 10
#define N 10000

template <class T>
//将根节点向下调整
void AdjustDown(T *top,int root)
{
    assert(root < K);
    size_t parent = root;
    size_t child = parent * 2 + 1;
    while (child < K)
    {
        if (child + 1 < K && top[child + 1] < top[child])
        {
            ++child;
        }
        if (top[child] < top[parent] && child < K)
        {
            swap(top[parent], top[child]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            return;
        }
    }
}

template <class T>
void GetTopK(T *arr,T *top)
{
    assert(K < N);
    for (size_t i = 0; i < K; i++)//取出N个数据的前K个数
    {
        top[i] = arr[i];
    }

    for (int j = (K - 2) / 2; j >= 0; j--)//创建一个有K个数据的小堆
    {
        AdjustDown(top, j);
    }

    for (size_t i = K; i < N; i++)//比较第K+1--第N个数据,找出最大的K个数
    {
        if (arr[i] > top[0])
        {
            swap(arr[i], top[0]);
            AdjustDown(top, 0);
        }
    }
}

template <class T>
void print(T *top)
{
    for (size_t i = 0; i < K; i++)
    {
        cout << top[i] << " ";
    }
    cout << endl;
}

void TestGetTopK()
{
    int arr[N] = { 0 };
    int top[K] = { 0 };
    for (size_t i = 0; i < N; i++)
    {
        arr[i] = i;
    }
    GetTopK(arr, top);
    print(top);
}

应用3.堆排序

以升序为例,堆排序首先建一个小堆,找到最小的数据,放到最后。通过向下调整算法(此时不用管数组的最后一个元素),找到最小的数据,放到最后,这样依次操作,可把数组排序。

#pragma once
#include <cassert>
#include <iostream>
#include <cstdlib>
using namespace std;

template <class T>
void Adjustdown(T* arr ,size_t root,size_t size)
{
    size_t parent = root;
    size_t child = parent * 2 + 1;
    while (parent < size)
    {
        if (arr[child] > arr[child + 1] && child + 1 < size)
        {
            ++child;
        }
        if (arr[child] < arr[parent] && child < size)
        {
            swap(arr[child], arr[parent]);
            parent = child;
            child = parent * 2 + 1;
        }
        else
        {
            break;
        }
    }
}

template <class T>
void HeapSort(T* arr, size_t size)
{
    assert(arr);
    for (int i = (size - 2) / 2; i >= 0; i--) //建小堆
    {
        Adjustdown(arr, i, size);
    }
    size_t end = size - 1;
    while (end > 0)
    {
        swap(arr[0], arr[end]);
        Adjustdown(arr, 0, end);
        end--;
    }
};
template<class T>
void print(T* arr,size_t size)
{
    for (int i = 0; i < size; i++)
    {
        cout << arr[i] << "  ";
    }
    cout << endl;
}
void TestHeapSort()
{
    int a[] = {7,1,0,5,8,2};
    HeapSort(a, sizeof(a)/sizeof(a[0]));
    print(a, sizeof(a) / sizeof(a[0]));
}

堆的性质
1、可以通过一个简单的数组实现
2、支持最坏情况为O(logN)的insert和deleteMin
3、支持常量平均时间的insert操作以及常量平均最坏时间的findMin操作。
4、二叉堆是实现优先级队列的典型方法
堆所支持的基本操作:

template<typename T>  
class BinaryHeap  
{  
       typedef HeapNode<T> Node;  
public:  
       BinaryHeap();  
       BinaryHeap(const vector<Node> &v);  
       void Insert(const Node& data);  
       void DeleteMin(Node& data = Node()); //删除最小元素,可以通过参数返回最小值  
       void MakeHeap();                     //置空堆  
       bool IsEmpty()const;                 //判断堆是不是为空  
       const Node& findMin()const;          //查找堆中的最小元素  
protected:  
       void buildHeap();                     //恢复堆的顺序  
       void percolate(int hole);             //从hole开始下滑调整  
protected:  
       vector<Node> _Heap;                   //存储的堆中元素的数组  
       int _Size;                             //标记堆中元素的个数  
};  

一、堆的建立
堆的建立有两种方式,一种是建立一个空堆,另一种是通过复制一个记录数组并对其加以调整形成一个堆。

BinaryHeap()  
       :_Heap(0)  
       , _Size(0)  
{}  

BinaryHeap(const vector<Node> &v)  
       :_Heap(v.size()+1)                               //为了便于计算,多开辟一个空间  
       , _Size(v.size())  
{  
       for (int i = 1; i <=_Size; i++)                //_Heap[0]不存放  
       {  
              _Heap[i] = v[i-1];  
       }  
       buildHeap();  
}  

时间复杂度:O(N*lgN)
二、Insert操作
Insert所需要的是上滑调整,直接向_Heap中插入数据,然后通过上滑调整顺序。

void Insert(const Node& data)  
{  
       _Heap.push_back(data);  
       _Size++;  
       int i = _Size;  
       int parent= 0;  
       for (; i/2>0; i /= 2)       //i这点的结点要有父亲节点  
       {  
              parent = i / 2;           //父亲结点的位置  
              if (_Heap[parent] < data)  //如果父亲结点比插入的值小,则有序  
              {  
                    break;  
              }  
              else                        //如果父亲结点比插入值大  
              {  
                    _Heap[i] = _Heap[parent];  
              }  
       }  
       _Heap[i] = data;  
}  

时间复杂度:O(lgN)

三、deleteMin操作
由于最小堆的性质,根节点总是最小值,所以将数组中最后一个结点的值放到根节点的位置,然后_Size自减,再从根节点开始进行一次下滑操作恢复堆的顺序。

void DeleteMin(Node& data=Node())          //删除最小元素,可以通过参数返回最小值  
   {  
          assert(_Size>0);  
          data = findMin();  
          _Heap[1] = _Heap[_Size--];  
          int i = 0;  
          percolate(1);                             //从根结点处开始下滑调整顺序  
   }  

时间复杂度:O(lgN)

完整代码:

#pragma once  
#include<cassert>  
#include<vector>  

template<typename T>  
struct HeapNode  
{  
    T _data;  
    size_t _key;  
    HeapNode()  
    {}  
    HeapNode(const T& data,size_t key)  
        :_data(data)  
        , _key(key)  
    {}  
    friend ostream& operator<<(ostream& os,const HeapNode<T>& heap)  
    {  
        os << heap._data;  
        return os;  
    }  
    friend bool operator>(const HeapNode<T>& h1,const HeapNode<T>& h2)  
    {  
        if (h1._key  > h2._key)  
            return true;  
        else  
            return false;  
    }  
    friend bool operator<(const HeapNode<T>& h1, const HeapNode<T>& h2)  
    {  
        if (h1._key < h2._key)  
            return true;  
        else  
            return false;  
    }  
};  


template<typename T>  
class BinaryHeap  
{  
    typedef HeapNode<T> Node;  
public:  
    BinaryHeap()  
        :_Heap(0)  
        , _Size(0)  
    {}  
    BinaryHeap(const vector<Node> &v)  
        :_Heap(v.size()+1)                               //为了便于计算,多开辟一个空间  
        , _Size(v.size())  
    {  
        for (int i = 1; i <=_Size; i++)                //_Heap[0]不存放  
        {  
            _Heap[i] = v[i-1];  
        }  
        buildHeap();  
    }  

    void Insert(const Node& data)  
    {  
        _Heap.push_back(data);  
        _Size++;  
        int i = _Size;  
        int parent= 0;  
        for (; i/2>0; i /= 2)       //i这点的结点要有父亲节点  
        {  
            parent = i / 2;           //父亲结点的位置  
            if (_Heap[parent] < data)  //如果父亲结点比插入的值小,则有序  
            {  
                break;  
            }  
            else                        //如果父亲结点比插入值大  
            {  
                _Heap[i] = _Heap[parent];  
            }  
        }  
        _Heap[i] = data;  
    }  

    void DeleteMin(Node& data=Node())          //删除最小元素,可以通过参数返回最小值  
    {  
        assert(_Size>0);  
        data = findMin();  
        _Heap[1] = _Heap[_Size--];  
        int i = 0;  
        percolate(1);                             //从根结点处开始下滑调整顺序  
    }  

    void MakeHeap()                            //置空堆  
    {  
        BinaryHeap<T> tmp;  
        swap(tmp._Heap ,_Heap);  
        _Size = 0;  
    }  

    bool IsEmpty()const                        //判断堆是不是为空  
    {  
        return _Size == 0;  
    }  

    const Node& findMin()const                 //查找堆中的最小元素  
    {  
        assert(_Size>0);  
        return _Heap[1];  
    }  
protected:  

    void buildHeap()                            //恢复堆的顺序  
    {  
        for (int i =_Size; i > 0; i--)  
        {  
            percolate(i);  
        }  
    }  

    void percolate(int hole)                           //从hole开始下滑调整  
    {  
        int child=0;  
        Node tmp = _Heap[hole];  
        for (;hole*2<=_Size;hole=child)                 //如果有左孩子则下滑调整  
        {  
            child = hole * 2;  
            if (child !=_Size&&_Heap[child]> _Heap[child + 1])   //找出左右孩子中值最小的  
                child++;  
            if (tmp < _Heap[child])                 //如果tmp小于孩子的值,则调整结束  
            {  
                break;  
            }  
            else                                    //如果tmp大于等于孩子的值,则继续调整  
            {  
                _Heap[hole] = _Heap[child];  
            }  
        }  
        _Heap[child] = tmp;  
    }  

protected:  
    vector<Node> _Heap;  
    int _Size;  
};  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值