堆排序
堆是完全二叉树,具有如下性质:大顶堆:每个节点的值大于或等于其左右孩子结点的值,小顶堆:每个节点的值都小于或等于其左右孩子结点的值。
堆排序是根据完全二叉树的性质5来排序的。
完全二叉树性质五:
1.如果i=1,则结点i是二叉树的根,无双亲,如果i>1,则其双亲是结点i/2
2.如果2*i>n,则结点i无左孩子,否则其左孩子是结点2*i
3.如果2*i+1>n,则该结点i无右孩子,否则其右孩子是结点2*i+1
堆排序的思想是,将待排序的序列构造成一个大顶堆。此时,整个序列的最大值就是堆顶的根节点,将它移走,此时末尾元素就是最大值,然后将剩下的n-1个序列重新构造成一个堆,这样就会得到n个元素中的次大值,如此反复,便能得到一个有序序列了。
程序思路:
1.将无序序列构建一个大顶堆。
2.将第一个元素和最后一个元素交换,将前n-1个元素重新构建大顶堆
3.循环往复,直到排序完成。
整体代码:
void Heap_sort(sqlist* l)
{
assert(l!=NULL);
int i;
for(i=l->length/2;i>0;--i)
{
Heap(l,i,l->length);
}
for(i=l->length;i>1;--i)
{
swap(l,1,i);
Heap(l,1,i-1);
}
构建大顶堆代码
void Heap(sqlist* l,int fi,int la)
{
int tmep,i;
temp=l->data[fi];
for( i=fi*2;i<la;i*=2)
{
if(i<la&&l->data[i]<l->data[i+1])
{
++i;
}
if(temp>l->data[j])
{
break;
}
l->data[fi]=l->data[i];
fi=j;
}
l->data[fi]=temp;
}
大顶堆代码解释:
1.输入参数为链表,起始位置,结束位置(一般为链表长队或顺序表长度)。
2.循环开始是从起始位置的左孩子开始,判断左右孩子哪个大,找到最大,用最大值和其父节点进行比较。
3.若孩子结点大,将该孩子结点的值赋值给双亲结点,并且将最大孩子结点的位置赋值给根节点,令其为根节点,再循环判断该根节点下的最大孩子结点。否则,跳出循环。
4.最终,将初始值赋值给交换位置后的点。
注意点:
1.在进行左右孩子结点比较时,一定要确保左右孩子结点存在。即左右孩子的坐标点不能超过链表或者顺序表的长度。
2.在查找下一级的最大/最小结点时,一定要判断该级结点的孩子结点在链表长度范围内。
3.赋值的时候是对最终改变了位置的点赋值。
顺序表的堆排序实现(小顶堆)
#define HeapElem int
void FilterDown(HeapElem* ar, int pos, int end)
{
int temp = ar[pos];
int i = pos;
int j = 2 * pos + 1;
while (j< end)
{
if (j + 1 <= end && ar[j] > ar[j + 1])
j += 1;
if (temp <= ar[j])
break;
ar[i] = ar[j];
i = j;
j = i * 2 + 1;
}
ar[i] = temp;
}
void swap(HeapElem* a, HeapElem* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void main()
{
int arr[10] = { 12, 23, 34, 45, 56, 67, 78, 89, 90, 2 };
int len = sizeof(arr) / sizeof(arr[0]);
int pos = 0;
while (pos!=(len-1))
{
FilterDown(arr, 0, len-1-pos);
swap(&arr[0], &arr[len - pos - 1]);
++pos;
}
for (int i = 0; i < len; ++i)
{
printf("%4d", arr[i]);
}
}
注意:这里有一个问题有待解决,2这个数字的排序是错误的,造成它错误的原因为,我们没有首先构造出一个小顶堆,所以它在排序时就发生了错误,所以更改时应该先从数组中间开始,向头部遍历,依次构建出一个小顶堆,再对构造好的小顶堆进行排序。
根据以上结论,我重新构建了一下,但还是没有解决问题!!!
void main()
{
int arr[10] = { 12,23, 34, 45, 56, 67, 78, 89, 90,2};
int len = sizeof(arr) / sizeof(arr[0]);
int pos = 0;
for (int i = len / 2; i > 0; --i)
{
FilterDown(arr, i, len - 1);
}
while (pos!=(len-1))
{
FilterDown(arr, 0, len-1-pos);
swap(&arr[0], &arr[len - pos - 1]);
++pos;
}
for (int i = 0; i < len; ++i)
{
printf("%4d", arr[i]);
}
}
还需要思考,来解决这个问题。