heapify操作,递归的将root结点的值与左右孩子的值进行交换,使得当前根节点值最大。
#include <stdio.h>
void swap(int *a, int *b)
{
if(NULL != a && NULL != b)
{
int temp = *a;
*a = *b;
*b = temp;
}
}
void heapify(int tree[], int n, int i) // 向下递归,使得根节点最大
{
if(i >= n) return ; // 递归出口
int c1 = i * 2 + 1;
int c2 = i * 2 + 2;
int max = i;
if(c1 < n && tree[max] < tree[c1])
{
max = c1;
}
if(c2 < n && tree[max] < tree[c2])
{
max = c2;
}
if(max != i)
{
swap(&tree[i], &tree[max]);
heapify(tree, n, max);
}
}
从根结点“下沉”并不能保证整个树成为最大堆。
比如:
我们发现,如果从先从“最后一个根结点”开始进行heapify,即先从倒数第一层heapify操作,就能保证倒数第一层中的根结点是最后一层中最大的。
而后从倒数第三层进行heapify操作,… 重复以上操作,直至第一层结点进行完heapify操作后,第一层的根节点是最大值。
而倒数第一层的根节点,也就是第一个非叶子结点也非常好确定。通过查询最后一个结点的父节点就可以找到。
void make_heap(int tree[], int n)
{
int last_node = n - 1; // 最后一个结点
int parent = (last_node - 1) / 2; // 最后一个非叶子结点
for( ; parent >= 0; parent--)
{
heapify(tree, n, parent);
}
}
接下来就是堆排序的操作了。
我们将第一层的根节点与最后一个叶子结点交换位置,那么最大值就是最后一个结点的值。“我们将最最后一个结点的值分离”。
而此时的第一层的根节点的值是交换后的结点值,不满足根最大原则。因此需要将此结点与其子树进行调整,而因为除了该根节点以外,其他子树都满足根最大的要求,即次几点的左右孩子必是整个树最大的结点之二,那么我们就只需要进行heapify操作即可。
重复以上操作即可完成堆排序。
void heap_sort(int tree[], int n)
{
make_heap(tree, n);
for(int i = n-1; i > 0; i--)
{
swap(&tree[0], &tree[i]);
heapify(tree, i, 0);
}
}
完成代码:
int main()
{
int arr[] = {4,6,3,1,7,9,4};
//int arr[] = {6,10,3,5,1,2};
int size = sizeof(arr) / sizeof(arr[0]);
for(int i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
//heapify(arr, size, 0);
//make_heap(arr, size);
heap_sort(arr, size);
for(int i = 0; i < size; i++)
{
printf("%d ",arr[i]);
}
printf("\n");
return 0;
}