线段树总结

线段树可以动态地处理可映射在坐标轴上的一些线段(区间),同时可以维护这些区间的某些性质。它的优点是可以随时插入一个区间或删除一个已有区间,并用较低的代价来维护需要的性质。一个线段是对应于一个区间的。线段树是区间树的一种。

1. 线段树是一棵二叉树。

2.叶结点表示一个初等区间[a, a];每一个内部结点(a,b)有b-a>=1;

3. 根为[a,b]的线段树为T(a,b),其左子树为T(a,(a+b)/2),右子树为T((a+b)/2+1,b),直至细分为一个初等区间(叶结点)为止。

4.树上的每个节点对应于一个线段(还是叫“区间”更容易理解,区间的起点和终点通常为整数),同一层的节点所代表的区间,相互不会重叠。

5.线段树是平衡树,它的深度为log(b-a)。

例如:

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


一般情况下都由数据域来执行线段树的用途。

一、线段树的指针表示形式

1.线段树基本性质和操作

线段树是一棵二叉树,记为T(a, b),参数a,b表示区间[a,b],其中b-a称为区间的长度,记为L。

线段树T(a,b)也可递归定义为:

若L>1 :  [a, (a+b) div 2]为 T的左儿子;

             [(a+b) div 2 + 1,b]为T 的右儿子。 

若L=1 : T为叶子节点。

线段树中的结点一般采取如下数据结构:

struct Node
{
    int   left,right;  //区间左右值
    Node   *leftchild;
    Node   *rightchild;    
};

线段树的建立:

Node   *build(int   l ,  int r ) //建立二叉树
{
    Node   *root = new Node;
    root->left = l;
    root->right = r;     //设置结点区间
    root->leftchild = NULL;
    root->rightchild = NULL;

    if ( l < r )
    {
       int  mid = (r+l) >>1;
       root->leftchild = build ( l , mid ) ;
       root->rightchild = build ( mid+1  , r) ; 
    } 

    return    root; 
}

线段树中的线段插入和删除

增加一个cover的域来计算一条线段被覆盖的次数,因此在建立二叉树的时候应顺便把cover置0。

插入一条线段[c,d]:

void  Insert(int  c, int d , Node  *root )
{
       if(c<= root->left && d>= root->right) 
           root-> cover++;
       else 
       {
           if(c <= (root->left+ root->right)/2 ) Insert (c,d, root->leftchild  );
           if(d > (root->left+ root->right)/2 ) Insert (c,d, root->rightchild  );
       }
} 

删除一条线段[c,d]:

void  Delete (int c , int  d , Node  *root )
{
       if(c<= root->left&&d>= root->right) 
           root-> cover= root-> cover-1;
       else 
       {
          if(c <= (root->left+ root->right)/2 ) Delete ( c,d, root->leftchild  );
          if(d > (root->left+ root->right)/2 ) Delete ( c,d, root->rightchild );
       }
} 

转自:http://www.cnblogs.com/shuaiwhu/archive/2012/04/22/2464583.html


举例说明:已知线段[2,5] [4,6] [0,7];求点2,4,7分别出现了多少次

三条已知线段插入过程:

[2,5]

--[2,5]插入【0,7】与3比较,分成两部分:插到左儿子【0,3】,插到右儿子【4,7】

--[2,5]插入【0,3】与1比较,插到右儿子【2,3】;[2,5]插入【4,7】和5比较,插到左儿子【4,5】

--[2,5]覆盖【2,3】区间,【2,3】记录+1;[2,5]覆盖【4,5】,【4,5】记录+1

[4,6]

--[4,6]插入【0,7】与3比较,插到右儿子【4,7】;

--[4,6]插入【4,7】与5比较,分成两部分,插到左儿子【4,5】,插到右儿子【6,7】

--[4,6]覆盖【4,5】,【4,5】记录+1;[4,6]插入【6,7】与6比较,插到左儿子【6,6】

--[6,6]覆盖【6,6】,【6,6】记录+1

[0,7]

--[0,7]覆盖【0,7】匹配,【0,7】记录+1

插入过程结束,线段树上的记录如下(红色数字为每条线段的记录n):

                                               【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

不管是插入操作还是查询操作,每次操作的执行次数仅为树的深度——logN


转自 http://hi.baidu.com/alpc62/item/be736a33a8864789f4e4ad18


线段树的题目:

http://blog.renren.com/share/279182900/1290893180

http://www.doc88.com/p-790936693134.html

http://poj.org/problem?id=2528

http://www.cppblog.com/sicheng/archive/2008/01/09/40791.html

http://www.cnblogs.com/Lyush/archive/2011/09/11/2173505.html

http://blog.csdn.net/shiqi_614/article/details/8228102

http://ichirin.blog.hexun.com/29222028_d.html


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值