主要看了这位大神的博客,地址>http://blog.youkuaiyun.com/morewindows/article/details/6709644/
觉得讲的很清楚,不过还是记录一下
堆的性质:
(最大堆)父节点大于任何一个子节点,因此堆顶元素一定最大
(最小堆),父节点小于任何一个子节点,因此堆顶元素一定最小
一般堆中不含重复数据吧~
堆排序
所谓堆排序,就是把满足性质的一个堆的堆顶元素取出,因为堆顶元素一定是当前堆中最大(最小)的。取出堆顶元素后,元素个数减少一个,同时堆的性质不在满足,因此需要维护堆使得其再次满足堆的性质。堆排序就是这样一个过程。
在文章>http://blog.youkuaiyun.com/morewindows/article/details/6709644/
中的插入和删除,我感觉更像是两种不同的维护堆性质的方法。
删除操作自底向上,插入自顶向下。
自底向上维护
注意,每一个元素都可以看出是以自己为尾节点的一个堆,也就是son为最后一个元素
// 新加入i结点 其父结点为(i - 1) / 2
void MinHeapFixup(int a[], int son)
{
int father, temp;
temp = a[son];
father = (son - 1) / 2; //父结点
while (father >= 0 && son != 0)
{
if (a[father] <= temp)
break;
a[son] = a[father]; //把较大的子结点往下移动,替换它的子结点
son = father;
father = (son - 1) / 2;
}
a[son] = temp;
}
那么插入
//在最小堆中加入新的数据nNum
void MinHeapAddNumber(int a[], int n, int nNum)
{
a[n] = nNum;
MinHeapFixup(a, n);
}
删除呢,删除的是顶部的元素
同样的,此时可以看出每个插入的 元素都是以自己为堆顶元素,而维护自己的堆,如果使用递归会更明显
// 从i节点开始调整,n为节点总数 从0开始计算 i节点的子节点为 2*i+1, 2*i+2
void MinHeapFixdown(int a[], int i, int n)
{
int j, temp;
temp = a[i];
j = 2 * i + 1;
while (j < n)
{
if (j + 1 < n && a[j + 1] < a[j]) //在左右孩子中找最小的
j++;
if (a[j] >= temp)
break;
a[i] = a[j]; //把较小的子结点往上移动,替换它的父结点
i = j;
j = 2 * i + 1;
}
a[i] = temp;
}
//在最小堆中删除数
void MinHeapDeleteNumber(int a[], int n)
{
Swap(a[0], a[n - 1]);
MinHeapFixdown(a, 0, n - 1);
}
对于一个无序数组,如何维护使其满足堆的性质?
两种方法,一种:一开始堆只有一个元素,显然满足性质。不断的往里面插入元素,每插入一个元素,都维护一次以当前元素为最后一个元素的堆。
另一种:当成删除元素,每次删除一个元素,都以当前元素为堆顶元素的堆。
由于没有子节点的元素一定满足堆的性质,因此可以从倒数第二层开始。
//建立最小堆
void MakeMinHeap(int a[], int n)
{
for (int i = n / 2 - 1; i >= 0; i--)
MinHeapFixdown(a, i, n);
}
注:本文代码来自>http://blog.youkuaiyun.com/morewindows/article/details/6709644/