数组模拟堆

本文用于个人算法竞赛学习,仅供参考

目录

一.什么是堆

二.堆的表示

 三.堆的存储

四.调整算法(小根堆为例)

1.向上调整算法--up()

 2.向下调整算法--down()

五.建堆

六.功能实现(小根堆为例)

1.插入一个数

2.求集合中的最值(小根堆求最小值)

3.删除最值(小根堆删除最小值)

4.删除任意一个元素(堆下标的位置和第k个插入的位置的元素)

5.修改任意一个元素(堆下标的位置和第k个插入的位置的元素)

七.整体代码

八.堆排序实现


一.什么是堆

堆是一种特殊的树形数据结构,常用于实现优先队列。堆分为最大堆和最小堆两种类型,最大堆要求父节点的值大于等于子节点的值,最小堆要求父节点的值小于等于子节点的值。

在堆中,通常使用数组来实现,具体实现时需要满足以下性质:
1. 父节点 i 的左子节点在位置2* parent+1,右子节点在位置2* parent+2;子节点 child 的父节点parent位置 parent = (child - 1) / 2(下标从0开始)

2.父节点 i 的左子节点在位置2* parent,右子节点在位置2* parent+1;子节点 child 的父节点parent位置 parent = child / 2(下标从1开始)

堆可以支持以下操作:
1. 插入操作:将新元素插入到堆的末尾,然后通过上浮操作(向上调整)将其调整到合适的位置;
2. 删除操作:通常删除堆顶元素,将堆末尾元素移动到堆顶,然后通过下沉操作(向下调整)将其调整到合适的位置;
3. 堆化操作:将一个无序数组调整为堆的过程,可以通过从最后一个非叶子节点开始进行下沉操作来实现。

堆的时间复杂度:
- 插入操作的时间复杂度为O(log n);
- 删除操作的时间复杂度为O(log n);
- 建堆的时间复杂度为O(n);
- 堆排序的时间复杂度为O(nlog n)。

堆在算法中有着广泛的应用,例如Dijkstra算法、Prim算法等。

二.堆的表示

堆的本质其实就是完全二叉树,除了底层节点可能没填满,其他层每个节点的度都为二叉树最大(2个),并且底层的节点都集中在底层靠左边若干位置(不靠左就不是),若底层为第h层,则最后一层可能有1~2^(h-1)个节点,对于堆来说,大根堆的根大于等于左右两个子节点,小根堆的根小于等于左右两个子节点。

具体关于二叉树的内容可以看往期文章:http://t.csdnimg.cn/7N8RZ

 三.堆的存储

这里用一维数组模拟,下标从1开始,会有

父节点 i 的左子节点在位置2* parent,右子节点在位置2* parent+1;子节点 child 的父节点parent位置 parent = child / 2

四.调整算法(小根堆为例)

调整算法有:向上调整和向下调整

1.向上调整算法--up()

 向上检查父节点是否符合条件,符合停止调整,不符合就继续向上调整。

//向上调整算法
void up(int u)
{
	//u / 2 判断是否存在父节点,第一个根节点下标是1,u / 2为0说明没有父节点
	while (u / 2 && h[u] < h[u / 2])
	{
		//传入下标--heap_swap是带映射的交换
		heap_swap(u, u / 2);
		//向上调整
		u = u >> 1;
	}
}

 2.向下调整算法--down()

 向下检查子节点是否符合条件,符合停止调整,不符合就继续向下调整。

//向下调整算法
void down(int u)
{
	int t = u;
	//判断左右孩子的较小值
	if (u * 2 <= sizes && h[u * 2] < h[t]) t = u * 2;
	if (u * 2 + 1 <= sizes && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
	if (u != t)
	{
		heap_swap(u, t);
		//向下调整
		down(t);
	}
}

五.建堆

建堆只需要在倒数第二层开始向下调整就可以了,建堆的时间复杂度O(n)

//建堆
void creat()
{
	for (int i = sizes / 2; i; i--)
	{
		down(i);
	}
}

六.功能实现(小根堆为例)

1.插入一个数

//插入一个数
void insert(int a)
{
	h[++sizes] = a;
}

2.求集合中的最值(小根堆求最小值)

//返回集合最小值
int min_heap()
{
	return h[1];
}

3.删除最值(小根堆删除最小值)

//删除最小值
void remove_min()
{
	heap_swap(1, sizes);
	sizes--;
	down(1);
}

4.删除任意一个元素(堆下标的位置和第k个插入的位置的元素)

//删除任意一个元素--传堆的下标
void remove_k(int k)
{
	heap_swap(k, sizes);
	sizes--;
	//调整有多种情况,下面只会执行一个调整
	down(k);
	up(k);
}

//删除第k个插入的元素
void del_k(int k)
{
	heap_swap(ph[k], sizes);
	sizes--;
	down(ph[k]);
	up(ph[k]);
}

5.修改任意一个元素(堆下标的位置和第k个插入的位置的元素)

//修改任意一个元素--传堆的下标
void change_k(int k,int a)
{
	h[k] = a;
	down(k);
	up(k);
}

//修改第k个插入的元素
void shift_k(int k, int a)
{
	h[ph[k]] = a;
	down(ph[k]);
	up(ph[k]);
}

前三个功能有STL实现,后面两个功能操作STL没有实现。

七.整体代码

//h[]存储堆中的值,h[1]是堆顶
//ph[]存储第k个插入的点在堆中的位置
//hp[]存储堆中下标是k的点是第几个插入的
//size维护数组h[]的大小
const int N = 10010;
int h[N], ph[N], hp[N], sizes;

//交换两个节点的值和映射关系--传入的是下标
void heap_swap(int a,int b)
{
	swap(ph[hp[a]], ph[hp[b]]);
	swap(hp[a], hp[b]);
	swap(h[a], h[b]);
}

//向上调整算法
void up(int u)
{
	//u / 2 判断是否存在父节点,堆顶下标是1,u / 2为0说明没有父节点
	while (u / 2 && h[u] < h[u / 2])
	{
		//传入下标--heap_swap是带映射的交换
		heap_swap(u, u / 2);
		//向上调整
		u = u >> 1;
	}
}

//向下调整算法
void down(int u)
{
	int t = u;
	//判断左右孩子的较小值
	if (u * 2 <= sizes && h[u * 2] < h[t]) t = u * 2;
	if (u * 2 + 1 <= sizes && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
	if (u != t)
	{
		heap_swap(u, t);
		//向下调整
		down(t);
	}
}

//建堆
void creat()
{
	for (int i = sizes / 2; i ; i--)
	{
		down(i);
	}
}

//插入一个数
void insert(int a)
{
	h[++sizes] = a;
}

//返回集合最小值
int min_heap()
{
	return h[1];
}

//删除最小值
void remove_min()
{
	heap_swap(1, sizes);
	sizes--;
	down(1);
}

//删除任意一个元素--传堆的下标
void remove_k(int k)
{
	heap_swap(k, sizes);
	sizes--;
	//调整有多种情况,下面只会执行一个调整
	down(k);
	up(k);
}

//删除第k个插入的元素
void del_k(int k)
{
	heap_swap(ph[k], sizes);
	sizes--;
	down(ph[k]);
	up(ph[k]);
}

//修改任意一个元素--传堆的下标
void change_k(int k,int a)
{
	h[k] = a;
	down(k);
	up(k);
}

//修改第k个插入的元素
void shift_k(int k, int a)
{
	h[ph[k]] = a;
	down(ph[k]);
	up(ph[k]);
}

八.堆排序实现

#include <iostream>
#include <algorithm>
using namespace std;

//h[]存储堆中的值,h[1]是堆顶
//size维护数组h[]的大小
const int N = 10010;
int h[N], sizes;

//交换两个节点的值和映射关系--传入的是下标
void heap_swap(int a,int b)
{
	//swap(ph[hp[a]], ph[hp[b]]);
	//swap(hp[a], hp[b]);
	swap(h[a], h[b]);
}

//向上调整算法
void up(int u)
{
	//u / 2 判断是否存在父节点,堆顶下标是1,u / 2为0说明没有父节点
	while (u / 2 && h[u] < h[u / 2])
	{
		//传入下标--heap_swap是带映射的交换
		heap_swap(u, u / 2);
		//向上调整
		u >>= 1;
	}
}

//向下调整算法
void down(int u)
{
	int t = u;
	//判断左右孩子的较小值
	if (u * 2 <= sizes && h[u * 2] < h[t]) t = u * 2;
	if (u * 2 + 1 <= sizes && h[u * 2 + 1] < h[t]) t = u * 2 + 1;
	if (u != t)
	{
		heap_swap(u, t);
		//向下调整
		down(t);
	}
}

//建堆
void creat()
{
	for (int i = sizes / 2; i ; i--)
	{
		down(i);
	}
}

//插入一个数
void insert(int a)
{
	h[++sizes] = a;
}

//返回集合最小值
int min_heap()
{
	return h[1];
}

//删除最小值--
void remove_min()
{
	heap_swap(1, sizes);
	sizes--;
	down(1);
}

int main()
{
	//插入10个数
	for (int i = 0; i < 10; i++)
	{
		int a;
		cin >> a;
		insert(a);
	}
	creat();
	//打印
	while (sizes)
	{
		cout << min_heap() << ' ';
		remove_min();
	}

	return 0;
}

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值