C++实现最大堆最小堆

目录

堆和栈的区别

最大堆与最小堆

最大堆的操作

最大堆的插入操作

最大堆的弹出操作

最大堆的C++代码实现

最小堆概念

最小堆的插入操作

最小堆的弹出操作

最小堆的C++代码实现

最大堆最小堆的应用

1.priority_queue

2. STL 堆操作


堆和栈的区别

  一、堆栈空间分配区别:
  1、栈(操作系统):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;
  2、堆(操作系统): 一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表。
  二、堆栈缓存方式区别:
  1、栈使用的是一级缓存, 他们通常都是被调用时处于存储空间中,调用完毕立即释放;
  2、堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。
  三、堆栈数据结构区别:
  堆(数据结构):堆可以被看成是一棵树,如:堆排序;

  栈(数据结构):一种先进后出的数据结构。

最大堆与最小堆

最大堆:根结点的键值是所有堆结点键值中最大者,且每个结点的值都比其孩子的值大。

最小堆:根结点的键值是所有堆结点键值中最小者,且每个结点的值都比其孩子的值小。

最小堆和最大堆的增删改相似,其实就是把算法中的大于改为小于,把小于改为大于。

最大堆的操作

在生成最大堆时,我们可以采取一次次遍历整棵树找到最大的结点放到相应的位置中。但是这种方法有个不好的地方,就是每个结点都要重复比较多次。

所以我们可以换一种思考的顺序,从下往上进行比较。先找到最后一个有子结点的结点,先让他的两个子结点进行比较,找到大的值再和父结点的值进行比较。如果父结点的值小,则子结点和父结点进行交换,交换之后再往下比较。然后一步步递归上去,知道所在结点的位置是0号位置跳出。这样就可以有效地减少比较所用到的时间。

但是每一次交换都要重复三步:temp = heap[i]; heap[i] = heap[j]; heap[j] = temp;

当树的结构比较大的时候,就会浪费很多时间。所以我们可以先把父结点的值存到temp中跟子结点比较,如果子结点大的话就把子结点的值赋值给父结点,再往下比较,最后才把temp的值存到相应的位置。

最大堆的插入操作

每当插入一个元素,将元素插入到容器(数组)尾部,然后执行迭代的上升操作,将插入的元素与其父节点比较,若比父节点大则交换,然后再与交换后节点的父节点相互比较交换,直到放置到合适位置。(最坏递归到根节点终止迭代)

最大堆的弹出操作

弹出最大值(即数组首地址元素 a[0])。先交换交换堆顶与堆末,再弹出堆末(最大值),然后再将现堆顶元素执行迭代的下降操作,若其子节点存在与其子节点比较,若比子节点小则交换,然后再与交换后的子节点相互比较交换,直到放置在合适位置。(最坏递归到叶子节点)

最大堆的C++代码实现

#include 
#include
using namespace std;

#define ElementType int

//构造大顶堆类
class MaxHeap
{
private:
	vector a;
public:
	//构造大顶堆
	MaxHeap(ElementType elements[], int number)
	{
		for (int i = 0; i < number; i++)
		{
			a.push_back(elements[i]);
		}
		for (int i = a.size() / 2 - 1; i >= 0; i--)
		{
			down(i);
		}
	}
	//交换i,j下标对应元素
	void swap(int i, int j)
	{
		ElementType temp = a[i];
		a[i] = a[j];
		a[j] = temp;
	}
	//上升操作
	void up(int i)
	{
		int fa = (i - 1) / 2;//f表示父节点下标
		if (i > 0 && a[i] > a[fa])
		{
			swap(i, fa);
			up(fa);//如果一直比父节点大,递归到根节点(下标0)
		}
	}
	//插入点
	void push(ElementType p)
	{
		a.push_back(p);
		up(a.size() - 1);//上升操作 直到合适位置
	}
	//下降操作
	void down(int i)
	{
		int son = 2 * i + 1;//son表示子节点下标
		if (son < a.size())
		{
			if (son + 1 < a.size() && a[son] < a[son + 1])
			{
				son++; //取值两个子节点 值更大的节点下标
			}
			//若子节点大,交换
			if (a[i] < a[son])
			{
				swap(i, son);
			}
			down(son);//递归调用,直到叶子节点
		}
	}
	//弹出堆顶节点(最大)
	ElementType pop()
	{
		ElementType result = a[0];
		swap(0, a.size() - 1);//交换堆顶与堆末
		a.pop_back();//弹出
		down(0);//再将原来的最末元素(现在的堆顶元素)下降操作,使之到合适位置
		return result;
	}
	//输出堆(顺序输出)
	void show()
	{
		for (int i = 0; i < a.size(); i++) {
			cout << " " << a[i];
		}
		cout << endl;
	}
};
int main(void)
{
	int a[] = { 13,43,84,96,22,65,70,47 };
	MaxHeap* m = new MaxHeap(a, 8);
	//测试
	cout << "显示当前最大堆:" << endl;
	m->show();
	cout << "弹出最大堆顶的元素:" << m->pop() << endl;
	cout << "显示当前最大堆:" << endl;
	m->show();
	cout << "插入一个节点90:" << endl;
	m->push(90);
	cout << "显示当前最大堆:" << endl;
	m->show();
	return 0;
}

最小堆概念

最小堆,是一种经过排序的完全二叉树,其中任一非终端节点的数据值均不大于其左子节点和右子节点的值。堆内的所有数据中最小的元素始终在堆顶,而增删一个元素而动态维护最小堆性质的时间复杂度仅为 O(logN)

最小堆的插入操作

每当插入一个元素,将元素插入到容器(数组)尾部,然后执行迭代的上升操作,将插入的元素与其父节点比较,若比父节点小则交换,然后再与交换后节点的父节点相互比较交换,直到放置到合适位置。(最坏递归到根节点终止迭代)

最小堆的弹出操作

弹出最小值(即数组首地址元素 a[0])。先交换交换堆顶与堆末,再弹出堆末(最小值),然后再将现堆顶元素执行迭代的下降操作,若其子节点存在与其子节点比较,若比子节点小则交换,然后再与交换后的子节点相互比较交换,直到放置在合适位置。(最坏递归到叶子节点)

最小堆的C++代码实现

#include 
#include
using namespace std;

#define ElementType int

class MinHeap
{
private:
	vector a;
public:
	//构造小顶堆
	MinHeap(ElementType elements[], int number)
	{
		for (int i = 0; i < number; i++)
		{
			a.push_back(elements[i]);
		}
		for (int i = a.size() / 2 - 1; i >= 0; i--)
		{
			down(i);
		}
	}
	//交换i,j下标对应元素
	void swap(int i, int j)
	{
		ElementType temp = a[i];
		a[i] = a[j];
		a[j] = temp;
	}
	//上升操作
	void up(int i)
	{
		int fa = (i - 1) / 2;//f表示父节点下标
		if (i > 0 && a[i] < a[fa])
		{
			swap(i, fa);
			up(fa);//如果一直比父节点小,递归到根节点(下标0)
		}
	}
	//插入点
	void push(ElementType p)
	{
		a.push_back(p);
		up(a.size() - 1);//上升操作 直到合适位置
	}
	//下降操作
	void down(int i)
	{
		int son = 2*i + 1;//son表示子节点下标
		if (son < a.size())
		{
			if (son + 1 < a.size() && a[son] > a[son + 1])
			{
				son++; //取值两个子节点 值更小的节点下标
			}
			//若子节点小,交换
			if (a[i] > a[son])
			{
				swap(i, son);
			}
			down(son);//递归调用,直到叶子节点
		}
	}
	//弹出堆顶节点(最小)
	ElementType pop()
	{
		ElementType result = a[0];
		swap(0, a.size() - 1);//交换堆顶与堆末
		a.pop_back();//弹出
		down(0);//再将原来的最末元素(现在的堆顶元素)下降操作,使之到合适位置
		return result;
	}
	//输出堆(顺序输出)
	void show()
	{
		for (int i = 0; i < a.size(); i++)
		{
			cout << " " << a[i];
		}
		cout << endl;
	}
};

int main(void)
{
	int a[] = { 13, 43, 84, 96, 22, 65, 70, 47 };
	MinHeap* m = new MinHeap(a, 8);
	//测试
	cout << "显示当前最小堆:" << endl;
	m->show();
	cout << "弹出最小堆顶的元素:" << m->pop() << endl;
	cout << "显示当前最小堆:" << endl;
	m->show();
	cout << "插入一个节点30:" << endl;
	m->push(30);
	cout << "显示当前最小堆:" << endl;
	m->show();
	return 0;
}

最大堆最小堆的应用

1.priority_queue

2. STL 堆操作

头文件是#include
一般用到这四个:make_heap()、pop_heap()、push_heap()、sort_heap();

你可能感兴趣的:(C/C++,嵌入式知识整理,最大堆,最小堆,数据结构,优先队列)