2016.5.8 神奇的博客
我又回来啦
如果有人问我是怎么瘦下来的,我会告诉他:“得着肠胃炎学数据结构,效果真是要上天!”
今天更的是堆的学习心得;
(弱化版)给你一些数,有两种操作:
{
第一种:输入c i d
把第i个数换成d
第二种:输入q
找所有数中的最大
}
正当我傻了吧唧的用着log(n)的暴力,发现n是1000000,操作总数是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的几次方。我们是把数据存储在最后,所以说要算出到底是要存在哪里。当你算出2的n次方时,前面的就是2的n次方减1,所以在读数据的时候就从2的n次方开始读就可以,当然在这里浪费一个int的数组中的下标[0]没有关系,我们在这道题中会从[1]开始用。(其实也没有什么关于数学的)
而这么做的好处就是只需要log(n)
如果有1024个数
只需要10次就可以修改完
如果1000000(1024取1000为近似值)个数
也只需要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!