题目来源POJ 3264
要求:在一组给定数值序列中,求给某个区间内的最大值和最小值的差。
对于初学者来说怎么理解
build(l, mid, n<<1);
build(mid + 1, r, n<<1|1)
s[n].maxx = max(s[n<<1].maxx, s[n<<1|1].maxx);
s[n].minn = min(s[n<<1].minn, s[n<<1|1].minn);
在递归构造左右 子孩子之后 s[n].maxx和s[n].minn进行保存的是左右孩子的最值 。
疑惑的地方在于 :这;两个数组怎么保证所求的正好就是同一层两个亲兄弟的最值呢?
比如说 n =3 时
build操作:
mid = (1+3)/2 = 2;
接着执行bulid (1,2,2)//此时不是根结点,可以对其进行扩充 ,假如右结点也需要对其进行扩充的话 安排4的结点树就是充裕的
递归调用:
mid = (1+2 )/2= 1;
build(1,1,4)发现左右子树相等;返回
可以对叶子结点用a[1-0]赋值了,赋的值是递归调用这一层的左结点或者右结点,利用这一层的L也就是上一层区间进行划分的一半。
build(2,3,4+1)//这里不确定 到底n是多少。这也直接影响了对递归调用的理解,与下来的顺序相反,对称。
mid = (2+3)/2 = 2;
build (2,2,4) 满足返回条件用a[1]返回。
执行bulid(3,3 ,3) 此时这一次的递归调用函数是在 左子树调用的末尾相对成(我个人理解为本来是从上到下,仍然从下构造到上)
发现 左右子树又相等了 直接用a[3-1]赋值后返回。
二叉树的结点特点 为
左结点 : 2*k;k为当前深度
右结点: 2*K+1;
下面是我引用的代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1000010
int n,a[N];
int ans_x, ans_y;
struct node
{
int r,l;//定义左右子树;
int maxx,minn;//存放左中右 这一组中的最值
}s[N<<2];//构造的结构体数组元素个数要是结点个数的4倍 线段树数组 随着构建二叉树会不断增加结点数
void build(int l, int r, int n) //n表示 的含义应该是 结点个数
{
s[n].l = l;
s[n].r = r;
s[n].maxx = 0;
s[n].minn = N;
if(l == r)//叶子结点
{
s[n].maxx = s[n].minn = a[l];//只有一个结点的情况,所以可以如此赋值
return;//因为修改了结点的值可以不返回一个具体的值
}
int mid = (l + r) >> 1; //进行拆分
build(l, mid, n<<1);//递归构造左子树
build(mid + 1, r, n<<1|1);//递归构造右子树
s[n].maxx = max(s[n<<1].maxx, s[n<<1|1].maxx); //对构造好的左右字数存储最值
s[n].minn = min(s[n<<1].minn, s[n<<1|1].minn);
}
//查找区间的内容
void query(int l, int r, int n)//l,r表示要查找的区间
{
if(s[n].l == l && s[n].r == r)
{
ans_x = max(ans_x, s[n].maxx);
ans_y = min(ans_y, s[n].minn);
return;
}
int mid = (s[n].l + s[n].r) >> 1;
if(r <= mid)//查找区间在当前区间的左半部分
query(l, r, n<<1);
else if(l > mid) //查找区间在当前位置的右半部分。
query(l, r, n<<1|1);
else//两部分都有的情况 分来两个方向遍历
{
query(l, mid, n<<1);
query(mid+1, r, n<<1|1);
}
}
int main()
{
int m,t,i,x,y;
while(cin>>m>>t)//输入 某个序列
{
for(i = 1; i <= m; i ++)
cin >> a[i];
build(1,m,1);//构造线段树
while(t--)
{
cin >>x >>y;
ans_x = 0;
ans_y = N;
query(x,y,1);
cout << ans_x-ans_y;
}
}
return 0;
}
自己的不足:对递归的思路不够清晰。在算法基础上掌握的不够扎实。