C++ 最小堆代码实现 详细注释

概述

二叉树相关

1、完全二叉树:若二叉树的深度为h,则除第h层外,其他层的结点全部达到最大值,且第h层的所有结点都集中在左子树。

2、满二叉树:满二叉树是一种特殊的的完全二叉树,所有层的结点都是最大值,即每层都是满节点状态。

1、堆是一颗完全二叉树;

2、堆中的某个结点的值总是大于等于(最大堆)或小于等于(最小堆)其孩子结点的值。

3、堆中每个结点的子树都是堆树。

4、每个堆的堆顶元素必然是堆中所有元素中最大值(最大堆)或最小值(最小堆)

下图所示为最小堆:
在这里插入图片描述

因为是完全二叉树结构,我们把堆顶下标置为1,后面的节点依次标号,可以发现这样一个规律:

左子节点的下标=父节点下标*2

右子节点的下标=父子节点下标*2+1

我们可以利用这样的规律利用数组来实现一个抽象的完全二叉树,从而形成一个堆

插入元素演示:
在这里插入图片描述插入元素结果:

在这里插入图片描述
删除堆顶元素演示:
在这里插入图片描述删除后:
在这里插入图片描述

详细原理见代码注释:

代码



#ifndef _HEAP_H
#define _HEAP_H

#include<iostream>
#include<vector>
#include<queue>
using namespace std;
//  ************* 输出堆 *************
void output_impl(int n, bool left, string const& indent, vector<int>& vec)
{
	if (n * 2 + 1 < (int)vec.size())
	{
		output_impl(n * 2 + 1, false, indent + (left ? "|     " : "      "), vec);
	}
	cout << indent;
	cout << (left ? '\\' : '/');
	cout << "-----";
	cout << vec[n] << endl;
	if (n * 2 < (int)vec.size())
	{
		output_impl(n * 2, true, indent + (left ? "      " : "|     "), vec);
	}
}
void output(vector<int>& v)
{
	if (v.size() <= 1)
		return;
	if (v.size() >= 4)
	{
		output_impl(3, false, "", v);
	}
	cout << v[1] << endl;
	if (v.size() >= 3)
	{
		output_impl(2, true, "", v);
	}
}
//  ***************************************
//指定最小堆
template<typename T>
class Less
{
public:
	bool operator()(const T& a, const T& b)const
	{
		return a < b;
	}
};

//指定最大堆
template<typename T>
class Greater
{
public:
	bool operator()(const T& a, const T& b)const
	{
		return a > b;
	}
};

//堆,默认最小堆less排序
template<class T, class Compare = Less<T> >
class Heap
{
private:
	int lastIndex;		//数组下标,从1开始计数
	Compare cmp;		//排序类,判断最大堆还是最小堆
	vector<T> vec;		//存储数组,vec[1]是堆顶
public:
	Heap() :lastIndex(1)
	{
		vec.push_back(T());   //预先分配空间,因为要用下标1作为堆顶
	}
	~Heap() {};
	//堆插入元素
	void push(T value)
	{
		vec.push_back(value);		//先插入尾部
		int curIndex = lastIndex;	//获取当前下标
		//插入元素上浮
		while (curIndex / 2)
		{
			if (cmp(vec[curIndex], vec[curIndex / 2]))  //cmp类来比较确定是最小堆还是最大堆
			{
				//需要上浮,说明插入元素比父节点更大(更小)
				swap(vec[curIndex], vec[curIndex / 2]);
				curIndex /= 2;
			}
			else
				break;

		}
		lastIndex++;
	}
	//获取堆中所有元素
	int size() { return lastIndex - 1; };

	//获取堆顶元素
	T top() { return empty() ? T() : vec[1]; };

	//判断是否为空堆
	bool empty() { return vec.size() <= 1; }
	//删除堆顶元素
	T pop()
	{
		if (lastIndex <= 1)
			return T();
		int curIndex = 1;
		T res = vec[1];
		vec[1] = vec[lastIndex - 1];	//为保证完全二叉树结构,把最后一个元素移到堆顶,然后执行下沉操作
		vec.pop_back();
		lastIndex--;
		//下沉操作
		while (curIndex * 2 <= lastIndex - 1)   //不要越界
		{
			int child = curIndex * 2;
			if (child + 1 <= lastIndex - 1 && cmp(vec[child + 1], vec[child]))  //找子节点合适值替换,比如最小堆就找两个子节点中最小的那个和父节点比较
			{
				child++;	//右子节点更小(更大)
			}
			if (cmp(vec[child], vec[curIndex]))  //子节点是否比父节点更大(更小)
			{
				swap(vec[child], vec[curIndex]);
			}
			else
				break;
			curIndex = child;
		}
		return res;	//返回删除堆顶元素
	}
	//打印堆元素
	void printHeap()
	{
		output(vec);
	}
};



int main()
{
	Heap<int, Greater<int>> hp;   //默认为最小堆,现在Greater<int>设为最大堆
	hp.push(3);
	hp.push(2);
	hp.push(1);
	hp.push(10);
	hp.push(20);
	hp.push(6);
	hp.printHeap();
	cout << "--------------pop()---------------" << endl << endl;
	hp.pop();
	hp.printHeap();
	cout << "--------------pop()---------------" << endl << endl;
	hp.pop();
	hp.printHeap();
	cout << "--------------top()---------------" << endl;
	cout << hp.top() << endl;
	cout << "--------------pop()---------------" << endl << endl;
	hp.pop();
	hp.printHeap();
	cout << "--------------pop()---------------" << endl << endl;
	hp.pop();
	hp.printHeap();


}


#endif // !MINHEAP_H


图形化输出堆

在这里插入图片描述

寻找数组中第k大的数(堆排序应用)

#include <iostream>
#include <string>
#include<vector>
using namespace std;
class Solution {
public:
	int findKthLargest(vector<int>& nums, int k) {
		int n = nums.size();
		//建立小根堆
		for (int i = 0; i < k; i++)
			swim(nums, i);
		for (int i = k; i < n; i++)
		{
			if (nums[i] > nums[0])
			{
				swap(nums[i], nums[0]);
				sink(nums, 0, k);
			}
		}
		return nums[0];
	}

	//上浮操作
	void swim(vector<int>& nums, int i)
	{
		while (i > 0 && nums[i] < nums[(i - 1) / 2])
		{
			swap(nums[(i - 1) / 2], nums[i]);
			i = (i - 1) / 2;
		}
	}
	//下沉操作
	void sink(vector<int>& nums, int i, int n)
	{
		int j = 0;
		while (i * 2 + 1 < n)
		{
			j = i * 2 + 1;
			if (j + 1 < n && nums[j + 1] < nums[j])
				j++;
			if (nums[i] < nums[j])
				return;
			swap(nums[i], nums[j]);
			i = j;
		}
	}
};
int main()
{
	vector<int> v{ 2,22,45,4,2,6,99,77 };
	Solution s;
	cout << s.findKthLargest(v, 3);   //找第三大的数
}

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值