堆及其应用

来源:互联网 发布:大学生网络创业问题 编辑:程序博客网 时间:2024/05/27 20:50

应用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 10000template <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
原创粉丝点击