[算法]线段树(IntervalTree)

   转载请注明出处:http://www.cnblogs.com/StartoverX/p/4617963.html 

  线段树是一颗二叉搜索树,线段树将一个区间划分成一些单元区间,每一个区间对应线段树的一个叶节点。对于线段树的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b]。因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度。

                                                                             

     线段树的特征:

  1.线段树的长度不超过logL(L是最长区间的长度)。

  2.线段树把区间上的任意一条线段都分成不超过2logL条线段。

  这些结论为线段树能够在O(logL)时间内完成一条线段的插入,删除,查找等工作,提供了理论依据。

    线段树的用途:

  线段树适用于和区间统计有关的问题。比如某些数据可以按区间进行划分,按区间动态进行修改,而且还需要按区间多次进行查询,那么使用线段树可以达到较快查询速度。

 数据结构:  

typedef struct IntervalTree
{
    int right;
    int left;
    IntervalTree* right_child;
    IntervalTree* left_child;
    /*other information*/
//通常为了解题需要记录其他的信息。
}IntervalTree;

    在线段上建树:

IntervalTree* buildTree(int a,int b)
{
    IntervalTree* tree = new IntervalTree;
    tree->right = b;
    tree->left = a;
    tree->right_child = NULL;
    tree->left_child = NULL;
    if(b > a)
    {
      int mid = (a + b)/2;
      tree->right_child = buildTree(mid+1,b);
      tree->left_child = buildTree(a,mid);
    }
    return tree;
}
//通常线段上会记录其他的信息,需要在建树时同时记录到树的每一个node。 

 

线段树通常引入为了解决关于区间的问题,在树中插入线段,删除线段,查询线段的方法都和具体问题有关,我们以下面的问题为例: 

已知范围A1....An,在该范围内不断进行插入新线段(Ai...Aj,1<=i<=j<=n),删除线段(Ai...Aj,1<=i<=j<=n)的操作,最后求某个线段被覆盖的次数?

 

为了解决这个问题,我们在线段A1...An上建树,并且在线段树中保存被覆盖的次数cover。

 

数据结构:  

typedef struct IntervalTree
{
    int right;
    int left;
    int cover;//保存被覆盖的次数。
    IntervalTree* right_child;
    IntervalTree* left_child;
}IntervalTree;

 

建树:

IntervalTree* buildTree(int a,int b)
{
    IntervalTree* tree = new IntervalTree;
    tree->right = b;
    tree->left = a;
    tree->cover = 0;//初始时cover为0
    tree->right_child = NULL;
    tree->left_child = NULL;
    if(b > a)
    {
      int mid = (a + b)/2;
      tree->right_child = buildTree(mid+1,b);
      tree->left_child = buildTree(a,mid);
    }
    return tree;
}

 

插入新线段:

void insert(int a,int b,IntervalTree* tree)
{
    if(tree->right == b && tree->left == a)
    {
        tree->cover++;
        return;
    }
    int mid = (tree->left + tree->right)/2;
    if(a > mid)
    {
        insert(a,b,tree->right_child);
    }
    else if(b <= mid)
    {
        insert(a,b,tree->left_child);    
    }
    else
    {
        insert(a,mid,tree->left_child);
        insert(mid+1,b,tree->right_child);
    }
}

 

删除线段:

void delete_interval(int a,int b,IntervalTree* tree)
{
    if(tree->right == b && tree->left == a)
    {
        tree->cover--;
        return;
    }
    int mid = (tree->left + tree->right)/2;
    if(a > mid)
    {
        delete_interval(a,b,tree->right_child);
    }
    else if(b <= mid)
    {
        delete_interval(a,b,tree->left_child);
    }
    else
    {
        delete_interval(a,mid,tree->left_child);
        delete_interval(mid+1,b,tree->right_child);
    }
}

 

查询线段覆盖次数:

int query(int a,int b,IntervalTree* tree)
{
    if(tree->right == b && tree->left == a)
    {
        return tree->cover;
    }
    int mid = (tree->right + tree->left)/2;
    if(a > mid)
    {
        return tree->cover + query(a,b,tree->right_child);
    }
    else if(b <= mid)
    {
        return tree->cover + query(a,b,tree->left_child);
    }
    else
    {
        int cover_right = query(mid+1,b,tree->right_child);
        int cover_left = query(a,mid,tree->left_child);
        return tree->cover + ((cover_right > cover_left) ? cover_right : cover_left);
    }
}

 

线段树应用举例:POJ 3264:Balanced Lineup:http://www.cnblogs.com/StartoverX/p/4618041.html

 

转载于:https://www.cnblogs.com/StartoverX/p/4617963.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值