首先呢,先来了解一个何为堆?

堆数据结构是一种数组对象,它可被视为一棵完全二叉树结构。

堆结构的二叉树存储是:

最大堆:每个父节点的都大于孩子节点

最小堆:每个父节点的都小于孩子节点

堆(插入,删除)_C/C++最大堆:a[] = {19,17,18,14,16,13,15,12,10,11}

最小堆:a[] = {10,11,13,12,16,18,15,17,14,19}


代码实现最大堆:

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

using namespace std;

template <class T>
class Heap
{
public:
	Heap()   //无参构造函数
	{}

	Heap(T* a,size_t size)
	{
		for(size_t i=0;i<size;++i)
		{
			_a.push_back(a[i]);
		}
		//建堆
		for(int i=(_a.size()-2)/2;i>=0;--i)
		{
			_ApDown(i);
		}
	}

	void Push(const T& x)//插入元素
	{
		_a.push_back(x);//在堆尾插入元素
		_ApHeapUp(_a.size()-1);	//向上调整	
	}

	void Pop()//删除(删除优先级高)
	{
		swap(_a[0],_a[_a.size()-1]);//交换堆的第一个元素和最后一个元素
		_a.pop_back();//删除最后一个元素
		_ApDown(0);//向下调整
	}

	size_t Size()//堆的大小
	{
		return _a.size();
	}

	bool Empty()//堆是否为空
	{
		return _a.empty();
	}

public:
	void _ApDown(size_t parent) 
	{
		size_t child = parent*2+1;
		
		while(child < _a.size())
		{
			//找到左右孩子中较大的       
			if((child+1) < _a.size() && _a[child+1] > _a[child]) 
			{
				++child;
			}
			//比较较大孩子与父亲
			if(_a[child] > _a[parent]) //(2)
			{
				swap(_a[child],_a[parent]);
				parent = child;
				child = parent*2+1;
			}
			else
			{
				break;
			}
		}
	}

	void _ApHeapUp(size_t child)
	{
		size_t parent = (child-1)/2;
		while(child > 0)
		{
			if(_a[child] > _a[parent])//比较孩子与父亲 (3)
			{
				swap(_a[child],_a[parent]);
				child = parent;
				parent = (child-1)/2;
			}
			else
			{
				break;
			}
		}
	}
	
protected:
	vector<T> _a;
};


若要实现最小堆,可将上述代码中(1)(2)(3)处的大于改为小于即可。

但是这样使得代码冗余,不容易维护。

那么如何用一个来实现即可建最大堆也可建最小堆呢?我们之前见的仿函数就用上场喽!

回顾一下什么是仿函数?

仿函数的机制就是通过对()进行重载。


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 Comper = Greater<T> >//默认建大堆
class Heap
{
public:
	Heap()   //无参构造函数
	{}

	Heap(T* a,size_t size)
	{
		for(size_t i=0;i<size;++i)
		{
			_a.push_back(a[i]);
		}
		//建堆
		for(int i=(_a.size()-2)/2;i>=0;--i)
		{
			_ApDown(i);
		}
	}

	void Push(const T& x)//插入元素
	{
		_a.push_back(x);//在堆尾插入元素
		_ApHeapUp(_a.size()-1);	//向上调整	
	}

	void Pop()//删除(删除优先级高)
	{
		swap(_a[0],_a[_a.size()-1]);//交换堆的第一个元素和最后一个元素
		_a.pop_back();//删除最后一个元素
		_ApDown(0);//向下调整
	}

	size_t Size()//堆的大小
	{
		return _a.size();
	}

	bool Empty()//堆是否为空
	{
		return _a.empty();
	}

public:
	void _ApDown(size_t parent) 
	{
		size_t child = parent*2+1;
		
		while(child < _a.size())
		{
			Comper com;
			//找到左右孩子中较大的
			if((child+1) < _a.size() && com(_a[child+1],_a[child]))
			{
				++child;
			}
			//比较较大孩子与父亲
			if(com(_a[child],_a[parent]))
			{
				swap(_a[child],_a[parent]);
				parent = child;
				child = parent*2+1;
			}
			else
			{
				break;
			}
		}
	}

	void _ApHeapUp(size_t child)
	{
		size_t parent = (child-1)/2;
		Comper com;
		while(child > 0)
		{
			if(com(_a[child],_a[parent]))//比较孩子与父亲
			{
				swap(_a[child],_a[parent]);
				child = parent;
				parent = (child-1)/2;
			}
			else
			{
				break;
			}
		}
	}
	
protected:
	vector<T> _a;
};

测试函数:

void Test1()
{
	int a[] = {10,16,18,12,11,13,15,17,14,19};
	//Heap<int> hp(a,sizeof(a)/sizeof(a[0]));  //默认建大堆
	Heap<int,Less<int> > hp1(a,sizeof(a)/sizeof(a[0])); //建小堆
}