堆排序半讲解


链接: 原文.

一,基础知识(记忆)

重要

若树的根节点记为 0
i处节点的left_child位置为:2 * i +1
i处节点的left_child位置为:2 * i +2
i处节点的parent位置为:( i - 1 )/ 2

大根堆 ,小根堆

1,为完全二叉树:添加新节点的顺序是:从上到下,从左到右

2,满足:父节点 > 子节点(小根堆相反)

3,可以用一个一维数组表示

二,heapify:堆调整

1,针对结点 i,将其两个子节点找出来,(越界的忽略)

2,找到这个最小单位的完全二叉树 的最大值,并将其交换至父节点的位置

3,递归调用,以便维护:交换后子节点与其子结点被破坏的堆关系,

4.递归出口:叶节点

5,结果:构造一个最小单位的完全二叉树对应的

// 交换结点位置
void swap(int a[], int i, int j)
{
    int t = a[i];
    a[i] = a[j];
    a[j] = t;
}

// 堆调整
void heapify(int tree[], int n, int i)
{
    if (i >= n)
        return;
    int c1 = 2 * i + 1;
    int c2 = 2 * i + 2;

    int max = i;
    if (c1 < n&&tree[c1] > tree[max])
        max = c1;
    if (c2 < n&&tree[c2] > tree[max])
        max = c2;
    if (max != i)
    {
        swap(tree, max, i);
        // 将最大值移至父节点
        heapify(tree, n, max);
        // 递归维护下面的结点
    }
}

二,build_heap:构造堆

由于 heapify 会递归调用维护下面的堆,所以想要构造完整的堆,
需要从下面往上构造:先找到最后一个父结点,再从最后一个父结点开始向上调用 heapify

// 构造堆
void build_heap(int tree[], int n)
{
    int last_node = n - 1;
    int parent = (last_node - 1) / 2;
    // 最后一个父结点
    int i;
    for ( i = parent; i >= 0; i--)
  // 从最后一个父结点开始向上调用 heapify
        heapify(tree, n, i);
}

三,heap_sort:利用堆进行堆排序

由堆可以得到什么呢 ?首先根结点是最大的,第二层的两个节点是 第二大和第三大的。

于是,利用这点就可以排序了

1,可以想到:既然根结点已经是最大的,那么就把这个数提走,剩下的再构造一个新堆。然后再提走根结点,再构造新的堆,循环往复。

2,但是这样就会面临一个问题?拿走根结点之后,根结点的数值由谁取代?

3,由于该结点要满足:移动该节点到根结点后,要对堆的结构破坏尽可能小,而且堆的大小还要因为结点少了一个进行减1(即 数组表示堆的范围要减 一)。显然最后一个结点是最好的选择了。移动后既不会影响到后面的结点,原来的位置还因为空了可以直接取消掉。

4,关键的 一步出现了。我们用最后一个结点替换根结点,此时根结点和第二层的两个结点构成一个最小单位的最小二叉树,此时就可以直接调用 heapify函数 ,形成一个新的堆。没有必要用 build_heap函数,因为此时最大值就在第二层,根结点会因为递归维护被替换到最后一层,其他结点也会因为递归维护而得到调整。

5,至于拿出来的结点放哪,当然还是放在原来的数组里咯。
由于数组前面存的是堆,于是可以从最后面的位置依次往前放,还可以形成升序序列。
用大根堆,因为我们想要的是升序序列,
如果想要降序序列的话,也可以用小根堆。

// 利用堆进行堆排序
//从小到大排序
void heap_sort(int tree[], int n)
{
    build_heap(tree, n);
    int i;
    for (i = n - 1; i >= 0; i--)
    // 每次取出根结点后,堆减少一个节点
    {
        swap(tree, i, 0);
        //交换根节点和最后一个节点
        heapify(tree, i, 0);
        // (假砍断)对从0到i的元素构造一个新堆,(i是指新堆节点数)
    }
}

主函数

int main(void)
{
    int i;
    int n;
    int m;
    scanf("%d %d",&n,&m);
    int tree[n+1] ;
    for(i=0; i<n; i++)
    {
        scanf("%d",&tree[i]);
    }


    //  build_heap(tree, n);
  //  heapify(tree,n,0); // 堆调整
   heap_sort(tree, n);

    for ( i =  n-1; i >=n-m+1; i--)
    {
        printf("%d ", tree[i]);
    }
    printf("%d\n", tree[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值