本文用于个人算法竞赛学习,仅供参考
目录
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;
}