堆的实现

堆数据结构是一种数组对象,它可以被视为一颗完全二叉树结构。
堆结构的二叉存储是
最大堆:每个父节点大于左右孩子节点
最小堆:每个父节点小于左右孩子节点

实现的基本接口
#include<vector>
template<class 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 i = (_a.size() - 2) / 2; i >= 0; --i)
              {
                      AdjustDown(i);
              }
       }
       void Push(const T& x)//时间复杂度logn
       {
              _a.push_back(x);
              AdjustUp(_a.size() - 1);
              
       }
       void Pop()//使得根消失,剩下的仍是大堆,时间复杂度logn
       {
              swap(_a[0], _a[_a.size() - 1]);
              _a.pop_back();
              AdjustDown(0);
       }
       bool Empty()
       {
              return _a.empty();
       }
       size_t Size()
       {
              return _a.size();
       }
       const T& Top()//取堆里的数据,返回一个对象,出了作用域对象还存在
       {
              return _a[0];
       }
       void Print(int* a, int k)//打印
       {
              for (int i = 0; i < k; i++)
              {
                      cout << a[i] << " ";
              }
              cout << endl;
       }
protected:
       /*T* a;
       size_t _size;
       size_t _capacity;*/
       vector<T> _a;
};

向下调整算法建大堆:
              第一步:找到最后一个非叶子节点
              第二步:找出当前节点的左右孩子中较大的一个节点(右孩子若存在则比较,否则不比较),让child指向该节点
              第三步:如果当前节点小于child就交换,下调,将孩子给父亲,孩子节点下移
              第四步:若不满足,则直接break。
void AdjustDown(size_t root)//建大堆:向下调整算法,时间复杂度:n*log2n
       {
              size_t parent = root;
              size_t child = parent * 2 + 1;
              while (child < _a.size())//child存在
              {
                      if ((child + 1<_a.size()) && (_a[child + 1] > _a[child]))//若child+1存在,则选左右孩子大的
                      {
                             ++child;
                      }
                      //此时拿到的孩子是大的
                      if (_a[child] > _a[parent])//若孩子比父亲大,则交换,且进行下一次调整
                      {
                             swap(_a[child], _a[parent]);
                             parent = child;
                             child = parent * 2 + 1;
                      }
                      else//大孩子小于父亲,则不需要调整
                      {
                             break;
                      }
              }
       }
向上调整算法:
                  第一步:上调,穿当前节点令其为孩子节点,上一节点为父节点。
                  第二步:若父节点小于当前节点,则交换;下调,将父亲给孩子,父亲节点上移
                  第三步:若不满足,则直接break。
   
void AdjustUp(size_t child)//向上调整算法,使得加一个数还是可调整成大堆,
{
              size_t parent = (child - 1) / 2;
              while (child>0)//若child小于0则调整终止
              {
                      if (_a[child] > _a[parent])
                      {
                             swap(_a[child], _a[parent]);
                             child = parent;
                             parent = (child - 1) / 2;
                      }
                      else
                      {
                             break;
                      }
              }
       }
测试:
void TestHeap()
{
       int a[] = { 10, 9, 13, 21, 18, 12, 11, 8, 14, 15 };
       Heap<int> hp1;//调用无参构造函数
       Heap<int> hp2(a, sizeof(a) / sizeof(int));
       
       hp2.Push(19);
       
       hp2.Pop();
}
         
堆的实现
push 19后的结果:
堆的实现
pop 21后的结果
堆的实现
建立大堆与小堆的基本思想是一致的,利用仿函数可直接创建大堆或小堆
#include<vector>
template<class T>
struct Less
{
       bool operator()(const T& l, const T& r)
       {
              return l < r;
       }
};
template<class T>
struct Greater
{
       bool operator()(const T& l, const T& r)
       {
              return l > r;
       }
};
template<class T,class Compare>
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 i = (_a.size() - 2) / 2; i >= 0; --i)
              {
                      AdjustDown(i);
              }
       }
       void Push(const T& x)//时间复杂度logn
       {
              _a.push_back(x);
              AdjustUp(_a.size() - 1);
       }
       void Pop()//使得根消失,剩下的仍是大堆,时间复杂度logn
       {
              swap(_a[0], _a[_a.size() - 1]);
              _a.pop_back();
              AdjustDown(0);
       }
       bool Empty()
       {
              return _a.empty();
       }
       size_t Size()
       {
              return _a.size();
       }
       const T& Top()//取堆里的数据,返回一个对象,出了作用域对象还存在
       {
              return _a[0];
       }
protected:
       void AdjustUp(size_t child)//向上调整算法,使得加一个数还是可调整成大堆,时间复杂度log2n
       {
              Compare com;
              size_t parent = (child - 1) / 2;
              while (child>0)//若child小于0则调整终止
              {
                      if (com(_a[child] , _a[parent]))
                      {
                             swap(_a[child], _a[parent]);
                             child = parent;
                             parent = (child - 1) / 2;
                      }
                      else
                      {
                             break;
                      }
              }
       }
       void AdjustDown(size_t root)//建大堆:向下调整算法,时间复杂度:n*log2n
       {
              Compare com;
              size_t parent = root;
              size_t child = parent * 2 + 1;
              while (child < _a.size())//child存在
              {
                      if ((child + 1<_a.size()) && (com(_a[child + 1] , _a[child])))//若child+1存在,则选左右孩子大的
                      {
                             ++child;
                      }
                      //此时拿到的孩子是大的
                      if (com(_a[child] , _a[parent]))//若孩子比父亲大,则交换,且进行下一次调整
                      {
                             swap(_a[child], _a[parent]);
                             parent = child;
                             child = parent * 2 + 1;
                      }
                      else//大孩子小于父亲,则不需要调整
                      {
                             break;
                      }
              }
       }
protected:
       /*T* a;
       size_t _size;
       size_t _capacity;*/
       vector<T> _a;
};
void TestHeap()//测试大堆的建立
{
       int a[] = { 10, 16, 18, 12, 11, 13, 15, 17, 14, 19 };
       Heap<int, Greater<int> > hp2(a, sizeof(a) / sizeof(int));
       hp2.Push(12);
       hp2.Push(20);
       hp2.Pop();
}

堆排序(选择排序)
 升序建大堆,降序建小堆
    (1)建堆
    (2)选出最大数\最小数
    (3)继续调整                                                                 
          堆的实现
代码实现:
#include<iostream>
using namespace std;

#include<assert.h>
void AdjustDowm(int *a, int k, int root)//建大堆
{
       int parent = root;
       int child = parent * 2 + 1;
       while (child < k)
       {
              if ((child + 1 < k)
                      &&(a[child + 1] > a[child]))//选左右孩子大的
              {
                      ++child;
              }
              if (a[child]>a[parent])
              {
                      swap(a[child], a[parent]);
                      parent = child;
                      child = parent * 2 + 1;
              }
              else
              {
                      break;
              }
       }
}
void HeapSort(int *a, int n)//堆排序
{
       assert(a);
       for (int i = (n - 2) / 2; i >= 0; --i)//大堆
       {
               AdjustDowm(a,n,i);
       }
       int end = n - 1;
       while (end > 0)//交换
       {
              swap(a[0], a[end]);
              AdjustDowm(a, end, 0);
              --end;
       }
}
void Print(int *a, int k)//打印
{
       for (int i = 0; i < k; ++i)
       {
              cout << a[i] << " ";
       }
       cout << endl;
}
void TestHeap()
{
int a[] = { 10, 16, 18, 12, 11, 13, 15, 17, 14, 19 };
       int k = sizeof(a) / sizeof(int);
       cout << "未排序的数据:" << " ";
       Print(a, k);
       cout << "排序后的数据:" << " ";
       HeapSort(a, k);
       Print(a, k);
}

int main()
{
       TestHeap();
       system("pause");
       return 0;
}

输出的结果:

        堆的实现