线段树的理论知识分析

/ /每个节点用结构体
struct line
{
int left,right;//左端点,右端点
int n;//记录这条线段出现了多少次,默认为0
}a[16];
//满二叉树的性质决定a[i]的左儿子是a[2*i],右儿子是a[2*i+1]
//对于已知的性段进行插入操作
void insert(int s,int t,int step)
//要插入线段的左端点,右端点,以及当前树的某条线段
{
if(s==a[step].left&&t==a[step].right)
{
a[step].n++;//插入线段匹配,此条线段记录加1;
return;;//插入结束返回
}
if(a[step].left==a[step].right)
       return ;//当前线段树没有儿子,为叶子节点
int mid=(a[step].left+a[step].right)/2;
if(mid>=t)  insert(s,t,step*2);
//中点在t的右边插入到左儿子,参考a[i],a[2*i]就能理解
else if(mid<=s)  insert(s,t,step+1);
//中点在t的左边插入到右儿子
else //中点在s和t之间,把待插线端分成两半分别插入到左右儿子里面
{
insert(s,mid,step*2);
insert(mid,t,step*2+1);
 }
}
//比如三条线段的插入情况:
//----[2,5]
//-[2,5]与[0,7]比较,分成两部分:[2,3]插到左儿子[0,3],[4,5]插到右儿子[4,7]
//[2,3]与[0,3]比较,插到右儿子[2,3],[4,5]和[4,7]比较,插到左儿子
//[2,3]与[2,3]匹配,[2,3]记录加1,[4,5]与[4,5]匹配,[4,5]记录加1;
//---[4,6]
//[4,6]与[0,7]比较,插到右儿子[4,7];
//[4,6]与[4,7]比较,分成两部分,[4,5]插到左儿子[4,5],[6,6]插到右儿子[6,7];
//[4,5]与[4,5]匹配,[4,5]记录加1;[6,6]与[6,7]比较,插到左儿子[6,6]
//[6,6]与[6,6]匹配,[6,6]记录加1;
//----[0,7]

//[0,7]与[0,7]匹配,[0,7]记录加1

                                                 【0,7】
                                                   1
                              /                                           \
                    【0,3】                                          【4,7】
                        0                                                    0
                /                \                                    /                \
       【0,1】                【2,3】               【4,5】               【6,7】
           0                          1                         2                        0
          /   \                     /      \                    /    \                   /      \
【0,0】 【1,1】 【2,2】 【3,3】 【4,4】 【5,5】 【6,6】 【7,7】
    0           0           0           0           0           0          1           0


询问操作和插入操作类似,也是递归过程,略

2——依次把【0,7】 【0,3】 【2,3】 【2,2】的记录n加起来,结果为2

4——依次把【0,7】 【4,7】 【4,5】 【4,4】的记录n加起来,结果为3

7——依次把【0,7】 【4,7】 【6,7】 【7,7】的记录n加起来,结果为1




struct line
{
int left,right;//左端点,右端点
int n;//记录这条线段出现了多少次,默认为0
}a[16];
//初始化新的节点
void buildt(int l,int r,int step)
//要重建的左右端点,以及当前线段树种某条线段
{
a[step].left=l;
a[step].right=r;
a[step].n=0;
if(l==r) return ;
buildt(l,(l+r)/2,step*2);//往下建左儿子
buildt((l+r)/2,r,step*2);//往下建右儿子
}
//访问
void count(int s,int t,int step)
{
if(a[step].n!=0)
{
sum=sum+a[step].n*(t-s+1);
}
if(a[step].left==a[step].right)  return;
int mid=(a[step].left+a[step].right)/2;
if(mid>=t)
  count(s,t,step*2);
else if(mid<s)
  count(s,t,step*2+1);
else
  {
  count(s,mid,step*2);
  count(mid+1,t,step*2+1);
  }
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值