影子宽度(线段树)

题目

桌子上零散地放着若干个盒子,桌子的后方是一堵墙。如右图所示。现在从桌子的前方射来一束平行光,把盒子的影子投射到了墙上。问影子的总宽度是多少?

  这道题目是一个经典的模型。可以把题目抽象地描述如下:x轴上有若干条线段,求线段覆盖的总长度。解决方法有很多:

解法

a.打暴力

  设线段坐标范围为[min,max]。使用一个下标范围为[min,max-1]的一维数组,其中数组的元素i表示[i,i+1]的区间。数组元素初始化为0。对于每一条区间为[a,b]的线段,将[a,b]内所有对应的数组元素设为1。最后统计数组中1的个数即可。
此方法的时间复杂度决定于下标范围和修改次数,缺点很明显。当下标的范围很大,并且修改次数很多时,此方法效率太低。

b.离散化

这道题也可以用离散化的思想去做,这里就不详细展开。

C.线段树(进入正题)

   在一类问题中,我们需要经常处理可以映射在一个坐标轴上的一些固定线段,例如说映射在X轴上的线段。由于线段是可以互相覆盖的,有时需要动态地取线段的并,例如取得并区间的总长度,或者并区间的个数等等。一个线段是对应于一个区间的,因此线段树也可以叫做区间树。 线段树是一种二叉树形结构,属于平衡树的一种。它将线段区间组织成树形的结构,并用每个节点来表示一条线段。树中的每一个结点表示了一个区间[a,b],每一个叶子节点表示了一个单位区间(如下图)。线段树的根节点表示了所要处理的最大线段区间,对于每一个非叶结点所表示的区间[a,b],其左子节点表示的区间为[a,(a+b)/2],右子节点表示的区间为[(a+b)/2,b]。 为了防止元素重复被取,我们一般用半开半闭的方式记录,这样表示的好处在于,处理的区间可能不是连续区间,可能是离散的点,这样就可以用[a,a+1)的节点来表示一个数组的元素,做到连续、离散的统一。

线段树中,线段即区间亦即树中结点。

在这里插入图片描述
  可以看到,对于每条要处理的线段,我们能够类似“二分”的进入线段树中处理,使得时间复杂度在 O(logN)量级,这也是线段树之所以高效的原因。

  • 线段树是一个平衡树,树的深度为log2(L-1)+1。
  • 线段树把区间上的任意一条长度为L的线段都分成不超过2 log L条线段的并。
  • 任两个结点要么是包含关系要么没有公共部分, 不可能部分重叠 。
  • 给定一个叶子p, 从根到p路径上所有结点代表的区间都包含点p,且其他结点代表的区间都不包含点p。
  • 结点数:假设根结点对应区间为[1, n],那么总结点个数不超过2n个。(由于线段树叶结点不超过n个,每个结点必然有2个孩子,所以非叶结点个数必定比叶结点个数少1。故总结点个数不超过2n个。

  在线段树的实际使用中,我们一般要给线段树的节点增加一些域。比如本题,我们可以增加一个cover域来记录本节点是否被完全覆盖。在这些域中保存了某种动态维护的信息,视不同情况而定,使得线段树具有极大的灵活性,可以适应不同的需求。

线段树的数据结构表示

完全二叉树 — 静态存储
Struct aty{
   
      int begin,end;  //区间
      int cover;   //覆盖情况域
}

链式结构 — 静态链/动态指针
//动态链
Struct aty{
   
       int cover;   //覆盖情况域
       int begin,end; //左右边界
       aty *lson , *rson;  //左右子节点指针
}
//动态链
Struct aty{
   
       int cover;   //覆盖情况域
       int begin,end; //左右边界
       aty *lson , *rson;  //左右子节点指针
}

另外我们也可以额外加上int mid来存储中点,储存这个点的好处是在每次在当前节点需要对线段分解的时候不需要再计算中点。

线段树的基本操作

建立

在对线段树进行操作前,我们需要建立起线段树的结构。我们使用结构体数组来保存线段树,这样对于非叶节点,若它在数组中编号为 num,则其左右子节点的编号为 2 * num,2 * num + 1。由于线段树是二分的树型结构&#x

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值