其实,单点修改和建树是差不多的,都是用到了递归具有回溯的性质。单点修改,具体而言:先向下找到要修改的叶子节点(不知道有没有不是修改叶子结点的情况),然后用 PushUp函数更新它的父亲结点,爷爷结点(我就想这样叫),曾爷爷结点,。。。。。。一直到开头的根结点。由于二叉树的性质,每层只用更新一个结点就够了。
第一张图是单点修改的
第二张图是创建树的f在这里插入图片描述有没有感觉他们很像?
持续更新中~~~~~
这个递归的性质,使得创建树的时候(用sum数组存储树),能遍历一遍数组a。所以,单点修改的时候,就是用的这个遍历的性质,将sum数组(结点)与修改的位置x(a的下标)对应起来。
持续更新中~~~~~X2
其实区间(例如 :1——9)的含义就是数组a的下标(下标为:1——9)。根节点1—9 代表这个结点存储的是1——9的相关信息。所以,一个叶子结点就只存储了数组a一个元素的信息。而树的结点又是用sum数组(当题目的要求是求和时)来存储的。所以,当l==r时(比如在build,即建树的函数里),会有sum【rt】=a【l】,因为l和r的含义是区间的端点,而区间里点的含义又是数组a某个点的下标。
还有,关于为什么r与l(区间)经过好多次折半的运算后,肯定会相等。这个应该是一个数学知识,就好像我们原来在高中里接触过的二分逼近,肯定会使两个数(两个区间端点)无线逼近一个实数。但是,我们这里只有正整数(只取正整数),所以运算一定次数之后,肯定会使两个数字(r,l)相等。
持续更新中~~~~3(想到啥说啥)
其实,建树,单点修改(比如给某个数增加一个数),(应该还有别的,只要是用到了递归,可能都属于这种情况)都是用到了回溯的性质来实现树的创建和维护(就是,对树的信息的更新)的。而关于上面提到的那两个,之所以一定要在函数里有形参 l 和 r 的原因,就是要通过形参,来遍历叶子结点。比如,单点修改(从判断条件就能看出来)。
``
//单点修改的代码(以增加一个数为例)
void Add(int pos,int l,int r,int rt,int num) //这些参数分别代表 要修改的点的下标,当前结点左右区
//间,当前树节点的下标,要增加的数字。
{
if(l== r&&pos==l) //到达叶子结点。//这里要格外理解。因为,l 和 r 是区间数字,也代表a数组的下标呀。
{
sum[rt]+=num;
return ;
}
int mid=(l+r)>>1;
if(pos<=mid) Add(pos,l,mid,rt<<1,num);
if(pos>mid) Add(pos,mid+1,r,rt<<1|1,num);
PushUp(rt);
}
/*关于这个功能,在main函数里的写法(个人感觉,看了在main函数里的写法
之后,更容易理解有关线段树的这些函数。)
*/
int main()
{
add(5,1,9,2); //这些参数分别代表 要修改的点的下标,当前结点左右区
//间,当前树节点的下标,要增加的数字。
}
**感谢**,,**XYX**大佬,和各位**学长们**为我们这些老鲜肉提供了大部分理论知识。