堆的定义
堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子结点的值。
如果父亲结点大于或等于孩子结点的值,那么称这样的堆为最大堆。反之,最小堆。以下都以最大堆作为实例。
int heap[100];
建堆
建堆的过程是不断调整的过程,每次调整都是把结点从上往下的调整(父结点和子结点的交换),即向下调整。
调整方法是这样的:总是将当前结点V与它的左右孩子比较(如果有的话),假如孩子中存在权值比结点V的权值大的,就将其中权值最大的那个孩子结点与结点V交换;交换完毕后继续让结点V和孩子比较,直到结点V的孩子的权值都比结点V的权值小或是结点V不存在。
时间复杂度为O(logN)。
//向下调整
//left为欲调整结点的数组下标,right一般为堆的最后一个元素的数组下标
void downAdjust(int left,int right)
{
int i = left,j = i*2; //i为欲调整目标,j为它的左孩子
while(j<=right) //存在孩子结点
{
//如果右孩子存在且大于左孩子的值
if(j+1<=right&&heap[j+1]>heap[j])
j = j+1; //更新将要进行比较和交换的坐标
//如果左右孩子中值最大的结点比欲调整的目标结点还要大
if(heap[j]>heap[i])
{
swap(heap[j],heap[i]); //交换
i = j; //更新欲调整目标
j = i*2; //更新左孩子
}
else break; //否则退出循环
}
}
建堆从倒数第一个拥有孩子的结点开始。即n的父结点n/2。
时间复杂度为O(N)。
//建堆
void creatHeap()
{
int i;
for(i=n/2;i>=1;i--)
downAdjust(i,n);
}
删除栈顶元素
如果要删除栈顶元素,并让其仍然保持堆的结构,那么只需要最后一个元素覆盖栈顶元素,然后对根结点进行调整即可。
时间复杂度O(logN)。
//删除栈顶元素
void deleteTop()
{
heap[1] = heap[n--]; //用最后一个元素覆盖栈顶元素,并将元素个数减一
downAdjust(1,n); //想下调整根结点
}
添加元素
如果想往堆里添加一个元素,可以把想要添加的元素放在数组最后,然后进行向上调整操作。向上调整总是把欲调整结点与父亲结点比较,如果权值比父亲结点大,那么就交换其与父亲结点,这样反复比较,直到达堆顶或者父亲结点的权值较大为止。
时间复杂度为O(logN)。
//向上调整,left一般为1,right一般为欲调整的下标
void upAdjust(int left,int right)
{
//i为欲调整的坐标,j为其父结点
int i = right,j = i/2;
while(j>=left) //父亲结点没有出界
{
if(heap[i]>heap[j]) //父亲结点的权值小于孩子结点的权值
{
swap(heap[i],heap[j]); //交换
i = j; //更新欲调整结点
j = i/2; //更新父亲结点
}
else break; //否则退出循环
}
}
void insertHeap(int x)
{
heap[++n] = x; //让元素先自加,再赋值
upAdjust(1,n); //向上调整,使添加元素后仍为堆结构
}
堆排序
时间复杂度是O(NlogN),不稳定。
顺便又拓展了一种排序算法【排序算法】冒泡排序|选择排序|插入排序|sort函数|归并排序|快速排序
堆排序的思路是——取出栈顶元素,然后将堆的最后一个元素替换至堆顶,再进行一次针对堆顶元素的向下调整。
void heapSort()
{
createHeap(); //先建堆
for(i=n;i>=1;i--) //倒着枚举
{
swap(heap[1],heap[i]); //交换堆顶元素
downAdjust(1,i-1); //向下调整未交换过的元素
}
}
这里给出堆排序的完整代码。
#include<cstdio>
#include<algorithm>
using namespace std;
int n,heap[1010];
void downAdjust(int left,int right)
{
int i = left,j = left*2;
while(j<=right)
{
if(j+1<=right&&heap[j+1]>heap[j])
j = j+1;
if(heap[j]>heap[i])
{
swap(heap[i],heap[j]);
i = j;
j = i*2;
}
else break;
}
}
void createHeap()
{
int i;
for(i=n/2;i>=1;i--)
downAdjust(i,n);
}
int main()
{
int i;
scanf("%d",&n);
for(i=1;i<=n;i++)
scanf("%d",&heap[i]);
createHeap();
for(i=n;i>1;i--)
{
swap(heap[1],heap[i]);
downAdjust(1,i-1);
}
for(i=1;i<=n;i++)
{
printf("%d",heap[i]);
if(i!=n) printf(" ");
}
return 0;
}
摘自《算法笔记》胡凡、曾磊著