AcWing 堆排序 (持续更新中···)

堆排序

由一颗完全二叉树组成。
小根堆:每个节点的值都小于等于它的左右儿子节点的值,根节点是最小的值
大根堆:每个节点的值都 大于等于它的左右儿子节点的值,根节点是最大的值

堆的功能(以手写的小根堆为例)

1、插入一个数
2、求集合中的最小值
3、删除最小值
4、删除任意一个元素
5、修改任意一个元素

P S PS PS:用 s i z siz siz 来维护当前堆的大小,每进行一次删除操作都要 s i z − 1 siz-1 siz1。另外,数组是以 1 1 1 开始的,所以 k k k 节点的左子树是 2 ∗ k 2*k 2k ,右子树是 2 ∗ k + 1 2*k+1 2k+1 。以 0 0 0 开始的话左子树就是 2 ∗ k + 1 2*k+1 2k+1 ,右子树是 2 ∗ k + 2 2*k+2 2k+2

down()操作

t是求左节点和右节点的之中最小值的下标,然后再向下递归

void down(int u)
{
    int t = u;
    if(u * 2 <= siz && h[u * 2] < h[t])
        t = u * 2;
    if(u * 2 + 1 <= siz && h[u * 2 + 1] < h[t])
        t = u * 2 + 1;
    if(t != u)
    {
        swap(h[t], h[u]);
        down(t);
    }
}

up()操作

不管是左子树还是右子树,他的下标除以2一定是他们的父亲节点

void up(int u)
{
    while(u / 2 && h[u] < h[u / 2])
    {
        swap(h[u], h[u / 2]);
        u/=2;
    }
}

删除最小值

先把根节点和最后一个点的值交换,再进行down()操作,注意要--siz

{
	arr[1]=arr[siz];
	--siz;
	down(1);
}

模板题链接:AcWing 839.模拟堆
A C AC AC代码里有两个数组,分别是 h p hp hp p h ph ph
p h ph ph表示第 k k k个插入的数在堆中什么位置,里面的参数是这个数是第几个被插入的
h p hp hp表示在第 n n n个位置上的数是第几个插入的,里面的参数是这个数是在堆中什么位置
本题的难点就在于如和快速找到第k个插入的数在堆里面的位置,也就是在堆里面的下标

void heap_swap(int a, int b)
{
    swap(ph[hp[a]],ph[hp[b]]);//此时ph里面的都是这个数是第几个插入的,返回的是这个数在堆中的位置
    swap(hp[a],hp[b]);//此时hp里面的是这个数在堆中的位置,返回的是这个数是第几个被插入的
    swap(h[a], h[b]);
}

顺利交换两个数,不仅要交换两个数在堆中的数值,还要在 h p hp hp数组里面交换这两个数在堆中的位置,以及 p h ph ph 数组里面是第几个被插入的
这里 h e a p _ s w a p heap\_swap heap_swap 里面的 a , b a,b a,b 参数都是表示这个数在堆中的位置,比如 t t t u u u 都是表示在堆中的位置。删除第 k k k 个数的时候,因为 k k k 已经被处理成了 k = p h [ k ] k=ph[k] k=ph[k] 。而 s i z siz siz 本来就表示它在队中最后一个位置,不用管。

这里以交换第 k k k 个数和最后一个 s i z siz siz 数为例:
首先 k k k 是表示第几个被插入, p h [ k ] ph[k] ph[k] 表示第 k k k 个插入的数在堆中什么位置, h p [ a ] hp[a] hp[a] h p [ b ] hp[b] hp[b] 分别表示 a , b a,b a,b 这两个位置的数是第几个被插入的,然后用 p h [ h p [ a ] ] ph[hp[a]] ph[hp[a]] ,和 p h [ h p [ b ] ] ph[hp[b]] ph[hp[b]] 交换这个第 k k k 插入的数和第 s i z siz siz 个插入的数的在堆中的位置,最后 s i z siz siz − 1 -1 1 的。然后用 h p [ a ] hp[a] hp[a] h p [ b ] hp[b] hp[b] 交换 a , b a,b a,b 两个位置的数是第几个被插入的这个第几个,最后交换两个值。始终记得我们的核心是要找到第k个插入的数在堆中的什么位置,而找到这个位置需要ph和hp两个数组共同维护,比如 k = p h [ k ] k=ph[k] k=ph[k] 的时候我们需要用到 p h ph ph 数组,而在 h e a p _ s w a p heap\_swap heap_swap数组里面的 p h ph ph 数组又需要 h p hp hp 数组维护
A C AC AC 代码如下:

#include<bits/stdc++.h>

using namespace std;
const int MAXN = 1e5 + 10;
int h[MAXN], ph[MAXN],hp[MAXN];//ph表示第k个插入的数在堆中什么位置
                                //hp表示在第n个位置上的数是第几个插入的
int N, siz, k, x;

void heap_swap(int a, int b)
{
    swap(ph[hp[a]],ph[hp[b]]);//此时ph里面的都是这个数是第几个插入的,返回的是这个数在堆中的位置
    swap(hp[a],hp[b]);//此时hp里面的是这个数在堆中的位置,返回的是这个数是第几个被插入的
    swap(h[a], h[b]);
}

void down(int u)
{
    int t = u;
    if(u * 2 <= siz && h[u * 2] < h[t])
        t = u * 2;
    if(u * 2 + 1 <= siz && h[u * 2 + 1] < h[t])
        t = u * 2 + 1;
    if(t != u)
    {
        heap_swap(t, u);
        down(t);
    }
}

void up(int u)
{
    while(u / 2 && h[u] < h[u / 2])
    {
        heap_swap(u, u / 2);
        u/=2;
    }
}

int main()
{
    int m = 0;
    scanf("%d", &N);
    while(N--)
    {
        string str;
        cin >> str;
        if(str == "I")
        {
            scanf("%d", &x);
            ++siz;
            ++m;
            h[siz] = x;
            ph[m] = siz;
            hp[siz]=m;
            up(siz);
        }
        else if(str == "PM")
            printf("%d\n", h[1]);
        else if(str == "DM")
        {
            heap_swap(1, siz);
            --siz;
            down(1);
        }
        else if(str == "D")
        {
            scanf("%d", &k);
            k = ph[k];
            heap_swap(k, siz);
            --siz;
            down(k);
            up(k);
        }
        else
        {
            scanf("%d%d", &k, &x);
            k = ph[k];
            h[k] = x;
            down(k);
            up(k);
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值