Book--数据结构 线段树 小结

2014-09-12 21:49:59  -> 2014-10-04 16:59:44

花了挺长的一段时间学习线段树,所刷的题:POJ线段树20题汇总,Hdu题库数道+比赛题2道。

与刚开始学连函数结构都要翻书相比,现在线段树已经实现自己较熟练地手敲。线段树这种结构,主要优势在于把区间问题从O(n)的复杂度优化到O(logn)。

几个注意点:

No.1:线段树的数组通常要开到叶子节点数的四倍(原因:刚好存满N个节点的线段树有趋近于2N个节点,而为了防溢出,要建出第N+1个叶子节点,那么根节点要再开一大棵子树出来,所以是4N)

No.2:有些线段树题目需要离散化,而线段树节点区间表示离散化后的值。

(1)建树,Build_tree

  1、叶子节点为点的情况:

    最为常见的建树方式,边界条件:l == r,递归方式:Build_tree(lp,l,mid) , Build_tree(rp,mid + 1,r)

  2、叶子节点为单位边的情况:

    这种情况中每两个相邻叶子有重叠点,如 (1,2) 和 (2,3),边界条件:l + 1 == r,递归方式:Build_tree(lp,l,mid) , Build_tree(rp,mid,r)

  3、节点赋值:

    可以在建树函数中嵌入读入语句,也可以赋值为离散化后的数组里的值

(2)更新,Update_tree

  1、点更新:

    最简单的更新方式,边界条件:l == r ,递归方式:根据点的相对位置只进入左 / 右子树中一棵

  2、(需查询型)区间更新:

    批量更改点,需要用到lazy思想,如果当前区间完全包含于需更新区间,则直接更新完并返回而不继续深入。这类更新需要在Query操作,并在其中判断是否继续深入。因此这种情况通常让每个树节点维护一个cover值,用来记录完全更新这个含义。

    边界条件:ql <= l && r <= qr

    递归方式:根据询问区间,if(ql <= mid) Update(左子树), if(qr > mid) Update(右子树)

  3、(无需查询型)精确区间更新:

    这类更新大多运用在扫描线中,精确更新的好处在于不用写查询函数,而每次查询只用到根节点的信息,这让扫描线写起来更飘逸。

    边界条件:ql == l && r == qr

    递归方式:因为是精确更新所以要控制好进入左右子树的区间范围(防止死循环)

     if(ql >= right_son_l) Update_tree(ql,qr,右子树) (左子树完全包含更新区间)

     else if(qr <= left_son_r) Update_tree(ql,qr,左子树) (右子树完全包含更新区间)

     else{ Update_tree(ql,left_son_r,左子树),Update_tree(right_son_l,qr,右子树)}

(3)查询,Query

    这个函数就大同小异了,边界条件:ql <= l && r <= qr,递归方式:if(ql <= mid) Query(左子树), if(qr > mid) Query(右子树)

(4)下推,Push_down

    这个函数对应于每个节点的cover值,是lazy思想的体现。在某此更新中如果发现所遍历到的点在之前cover非零(即有区间完全更新的含义),那么就需要Push_down函数来把当前区间的信息下推给它的两个子树。注意点:下推完后自己的cover值要归零。(这里归零的含义应理解为标记为没有区间完全更新的含义)

 

几个经典题目:POJ 3667,这题把区间操作考察了个遍,然后每个节点需要维护的值也很多。

       POJ 3695,扫描线好题,写完这题,扫描线基本上没问题了。

转载于:https://www.cnblogs.com/naturepengchen/articles/3969146.html

你可能感兴趣的:(数据结构与算法)