堆应用

2016.5.8 神奇的博客 我又回来啦

如果有人问我是怎么瘦下来的,我会告诉他:得着肠胃炎学数据结构,效果真是要上天!今天更的是堆的学习心得;

(弱化版)给你一些数,有两种操作:

第一种:输入c i d 把第i个数换成d

第二种:输入q 找所有数中的最大

 

正当我傻了吧唧的用着log(n)的暴力,发现n1000000,操作总数是10000的时候。。。所以说用暴力是绝对绝对不可以的啦!这时就选择了-------堆。

            1

          /      \

         2        3

      /   \       /   \

   4     5      6    7

  /  \    /  \    /   \     /   \

8   9 10 11 12 13 14 15


其实呢,我们只用最下面的一行来存储数据,那么上面的都是干什么的嘞?


                15

           /             \

         11            15

        /   \           /   \

      6    11      15    3

     /  \    /  \    /   \     /   \

    6   2  7 11 8 15    3  0

(博主的树好丑。 [汗。。。])把我的数据放进去。可以看出每个节点就是左子和右子的两个数中的最大值。也就是说如果你取所有数的最大值只是log(1), log(1)!!!只需要把最上方的取出来即可。


如果是修改呢?其实说这个方法和暴力是相反的,暴力就是修改容易取值难,但是堆是取值容易修改难。

                15

           /             \

         11            15

        /   \           /   \

      6    11      15    3

     /  \    /  \    /   \     /   \

    6   2  7 11 8 15    3  0


假设我们修改的是3,修改成19的话,

                15

           /             \

         11            15

        /   \           /   \

      6    11      15       3

     /  \    /  \    /   \     /   \

    6   2  7 11 8 15  19   0

这是不可能的,因为你需要把上面所有的区间跟它有关的全部修改。也就是说要递推上去,修改。

                19(15)

           /             \

         11            19(15)

        /   \           /   \

      6    11      15    19(3)

     /  \    /  \    /   \       /   \

    6   2  7 11 8 15 19(3)  0

有关于数据结构的第一部分结束

-----------------------------

下面的就是数学方面的了

因为虽然这像一棵树,但是我们是不会选择用结构体建树的,而是选用数组。如果说选用数组的话,我们就要碰到一个问题了:

                                             下标

我们所简历的是一个满二叉树,所以每行的都是2的几次方。我们是把数据存储在最后,所以说要算出到底是要存在哪里。当你算出2n次方时,前面的就是2n次方减1,所以在读数据的时候就从2n次方开始读就可以,当然在这里浪费一个int的数组中的下标[0]没有关系,我们在这道题中会从[1]开始用。(其实也没有什么关于数学的)

而这么做的好处就是只需要log(n) 

如果有1024个数 只需要10次就可以修改完

如果100000010241000为近似值)个数 也只需要20

等等 就非常非常高效率了

-----------------------------

//以此奉上代码

#include <cstdio>

#include <cstdlib>

#include <iostream>

#define MAXN 100100

using namespace std;

int n,d[3*MAXN],q,x,y;

char c;

int main()

{

scanf("%d",&n);

int L=1;

while(L <n) L+=L; //找到合适的2的次方

for(int i=L;i<=L+n-1;i++)

    scanf("%d",&d[i]);//输入

for(int i=L-1;i>=1;i--)

    d[i]=max(d[2*i],d[2*i+1]);//取左右子的大数 取左子只需要2*i 右子是2*i+1 从下往上推

scanf("%d",&q);

for(int i=1;i<=q;i++)

{

getchar();//数字和字符混输别忘了getchar

scanf("%c",&c);

if(c=='C')

{

scanf("%d%d",&x,&y);

d[L+x-1]=y;//替换

x=(L+x-1)/2;//上面的节点

while(x) d[x]=max(d[2*x],d[2*x+1]),x/=2;//节点不为0 就是没到头

}

else if(c=='Q')

    printf("%d\n",d[1]);//log(1)的取最大值,非常简洁

}

return 0;

}

//就到这里,过几天会陆续奉上正常版和加强版,see you next time!


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值