二叉堆
二叉堆是基于完全二叉树的基础上,加以一定的条件约束的一种特殊的二叉树。
根据约束条件的不同,二叉堆又可以分为两个类型:
大顶堆和小顶堆。
大顶堆
即任何一个父节点的值,都 大于等于 它左右孩子节点的值。
小顶堆
即任何一个父节点的值,都 小于等于 它左右孩子节点的值。
二叉堆的根节点叫做 堆顶 ,它是大顶堆里面的最大值,小顶堆里的最小值。
堆的操作
大顶堆和小顶堆的操作都是差不多的,这里用小顶堆来讲。
建立二叉堆
假如我们有一个二叉树,如下图:
我们从最下面一个 非叶子节点开始 ,是3。
因为小顶堆的父节点要比子节点要小,所以3可以不动,同理0也不动。
然后我们到了9,我们把9和它两个子节点中最小的那个换位置:
然后9继续和子节点交换:
这里其实做的是一个让非叶子节点 下沉 的操作。
然后我们接着把5下沉:
5接着下沉:
然后我们这个小顶堆就完成了。
堆的建立其实就是根据约束条件 下沉非叶子节点 的过程。
删除节点
堆节点的删除,是指将堆顶删除,如图:
因为二叉堆是建立在完全二叉树的基础上。
所以为了维护完全二叉树,我们将最后一个节点移动到堆顶:
然后我们根据约束规则,把9下沉就行了,最后结果是下图:
添加节点
建立和删除都是 下沉 操作,添加不一样,它做的是 上浮 操作。
假设我们要添加一个0,我们先将0最为最后一个节点,如图所示:
上浮 操作:
将 上浮节点 和其 父节点 比较,如果上 浮节点<父节点 ,就互换。
首先是0和6互换:
然后0和3换位置,再和2换位置,得到最终结果:
堆排序
堆排序非常简单。
因为堆顶是最值,所以我们可以把堆顶和最后一个节点换位置,然后再把最后节点从树上摘除。
此时我们得到了一个新的树,我们再把这个树重新变成二叉堆。
然后堆顶又是最值了,接着重复上述步骤,就能实现堆排序。
代码实现
因为堆是基于完全二叉树,所以我们不需要用链式结构来表示,我们可以直接用数组存。
假设父节点的下表为parent,从父节点获取子节点:
左节点下标: 2*parent+1
右节点下标: 2*parent+2
假设子节点的下标为son(左右子节点都可以):
父节点下标:(son-1)/2
所以我们可以非常简单的表示出节点之间的关系了,下面是代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 10000;
int heap[maxn];
int size = 0;
int getLeft(int parent) //获取左节点
{
return 2 * parent + 1;
}
int getRight(int parent) //获取右节点
{
return 2 * parent + 2;
}
int getParent(int son) //获取父节点
{
if (son <= 0)
return -1;
return (son - 1) / 2;
}
void initHeap(int n) //n是堆的大小,初始化堆元素
{
int index = n;
while (index--) //获取最后一个非叶子节点
if (getLeft(index) < n)
break;
while (index >= 0)
{
int parent = index, temp = heap[parent];
while (true)
{
int left = getLeft(parent), right = getRight(parent);
if (left >= n) //没有左节点
break;
int minSon = right >= n ? left : heap[left] < heap[right] ? left
: right;
if (temp <= heap[minSon]) //父节点小于等于最小子节点
break;
heap[parent] = heap[minSon];
parent = minSon;
}
heap[parent] = temp;
index--;
}
}
void creatHeap(int n) //创建堆
{
for (int i = 0; i < n; i++)
cin >> heap[i];
size = n;
initHeap(size);
}
void insert(int value) //添加元素
{
int son = size;
heap[size++] = value; //先将插入的数据放在最后
while (true)
{
int parent = getParent(son); //得到它的父节点
if (parent < 0) //son已经是根节点了,结束
break;
if (heap[parent] <= value) //父节点小于等于插入的值,结束
break;
heap[son] = heap[parent];
//然后让父节点变成下一轮的子节点,向上继续比较
son = parent;
}
heap[son] = value;
}
void pop() //删除元素
{
if (size <= 0)
return;
heap[0] = heap[--size];
int parent = 0, temp = heap[parent];
while (true)
{
int left = getLeft(parent), right = getRight(parent);
if (left >= size) //parent没有子节点了
break;
int minSon = right >= size ? left : heap[left] < heap[right] ? left
: right;
if (temp <= heap[minSon]) //父节点小于等于最小子节点
break;
heap[parent] = heap[minSon];
parent = minSon;
}
heap[parent] = temp;
}
void printHeap() //打印堆
{
for (int i = 0; i < size; i++)
cout << heap[i] << ' ';
cout << endl;
}
void heapSort() //堆排序
{
if (size <= 0)
return;
int n = size;
while (n--)
{
swap(heap[0], heap[n]);
// printHeap();
initHeap(n);
}
}