为了自己的方便记忆吧,复习了一下堆这种数据结构。写一遍就会加深印象。
堆,是一种完全二叉树,所以具有完全二叉树的所有性质,在这里我们会用到的最重要的一条性质就是,若设完成二叉树共有n个节点,那么,最后一个非叶节点是n/2。(注意:编程时,一般我们用数组的下标是从0开始的)我们可以利用这个性质,在构建堆和筛选的过程中,不必去遍历所有的节点,从而节省了时间。(为什么呢?因为:从第n/2+1个节点到第n个节点都为叶子节点,我们在筛选和构建堆时,就是要比较父节点和子节点的值,现在叶子节点是没有孩子的,所以不用去遍历。)
堆,分为大根堆和小根堆。如果父亲节点的值大于左右子孩子的值,而且孩子节点也满足此性质,那么就是大根堆,相反就是小根堆。堆的构建和筛选过程,是一个逐步向根节点走的过程,这样可以保证每一个节点都满足堆的性质。
我们以小根堆为例吧。我懒得画图来分析,别的博客上都有很详细的介绍,我直接上代码吧。构建堆的代码,可以用递归和非递归,两种我都写了一遍,以下代码在本人电脑上visual studio2010运行正常,若有发现错误,请批评指正。
非递归:
void CreateHeap(int data[],int len) // 非递归
{
int p = len / 2 - 1;
while(p >= 0 && p < len)
{
int q = 2 * p + 1;
if(q < len)
{
if(q < len - 1 && data[q+1] < data[q])
q++;
if(data[q] < data[p])
{
int tmp = data[p];
data[p] = data[q];
data[q] = tmp;
int n = 2 * q + 1;
if((n < len && data[n] < data[q]) || (n < len - 1 && data[n+1] < data[q]))
p = q;
else
p--;
}
else
p--;
}
}
}
递归:
void CreateHeapR(int data[],int p, int len) // 递归
{
if(p >= 0 && p < len)
{
int q = 2 * p + 1;
if(q < len)
{
if(q < len - 1 && data[q+1] > data[q])
q++;
if(data[q] > data[p])
{
int tmp = data[p];
data[p] = data[q];
data[q] = tmp;
int n = 2 * q + 1;
if((n < len && data[n] > data[q]) || (n < len - 1 && data[n+1] > data[q]))
CreateHeapR(data,q,len);
else
CreateHeapR(data,--p,len);
}
else
CreateHeapR(data,--p,len);
}
}
}
以上就是堆的构建和筛选过程。而对于堆排序,我们可以直接利用上面的函数来实现。用堆排序的过程就是:先将二叉树的根节点输出,然后,将最后一个叶子节点赋给根节点,并进行筛选。重复以上步骤,直到输出所有的节点。以小根堆为例,这个输出的序列就是一个有序的序列。堆构造和筛选过程时间复杂度为O(logn),而排序过程需要n次的筛选过程,所以时间复杂度为O(n*logn)。
堆排序代码:
void HeapSort(int data[],int len)
{
if(len <= 1)
return;
CreateHeap(data,len);
while(len)
{
cout << data[0] << " ";
data[0] = data[len-1];
len--;
CreateHeap(data,len);
}
}