堆排序简要介绍

本文详细介绍了堆排序算法的工作原理及其实现过程。通过构建大顶堆或小顶堆,利用完全二叉树的特性进行排序。文章提供了堆排序的代码示例,并针对小顶堆排序中出现的问题进行了探讨。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

堆排序

堆是完全二叉树,具有如下性质:大顶堆:每个节点的值大于或等于其左右孩子结点的值,小顶堆:每个节点的值都小于或等于其左右孩子结点的值。
堆排序是根据完全二叉树的性质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]);
    }
}

还需要思考,来解决这个问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值