[算法]线段树(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

时间: 2024-10-29 16:48:36

[算法]线段树(IntervalTree)的相关文章

[莫队算法 线段树 斐波那契 暴力] Codeforces 633H Fibonacci-ish II

题目大意:给出一个长度为n的数列a. 对于一个询问lj和rj.将a[lj]到a[rj]从小到大排序后并去重.设得到的新数列为b,长度为k,求F1*b1+F2*b2+F3*b3+...+Fk*bk.当中F为斐波那契数列.F1=F2=1.对每一个询问输出答案模m. 区间查询离线 用莫队算法 开棵权值线段树,然后用斐波那契的性质update F(n+m)=F(n+1)*F(m)+F(n)*F(m-1); #include<cstdio> #include<cstdlib> #includ

POJ 3368 Frequent values RMQ ST算法/线段树

                                                     Frequent values Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 15229   Accepted: 5550 Description You are given a sequence of n integers a1 , a2 , ... , an in non-decreasing order. In

HDU 4638 Group (莫队算法||线段树离散查询)

题目地址:HDU 4638 先写了一发莫队,莫队可以水过.很简单的莫队,不多说. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorithm> #include <stdlib.h> #include <map> #include <set> #include <s

[算法]线段树

拖了好久才写的线段树...... 大概听说它可能实在n年前,在我还是一个孩子的时候[/微笑] 恩大概我觉得有一丢丢丢像分块 ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 一 概述 线段树,类似区间树,它在各个节点保存一条线段(数组中的一段子数组)

笔试算法题(42):线段树(区间树,Interval Tree)

议题:线段树(Interval Tree) 分析: 线段树是一种二叉搜索树,将一个大区间划分成单元区间,每个单元区间对应一个叶子节点:内部节点对应部分区间,如对于一个内部节点[a, b]而言,其左子节点表示的区间为[a, (a+b)/2],其右子节点表示的区间为[1+(a+b)/2, b]: 对于区间长度为N的线段树,由于其单元节点都是[a, a]的叶子节点,所以其叶子节点数为N,并且整棵树为平衡二叉树,所以总节点数为2N-1,树的深度为log(N)+1: 插入操作:将一条线段[a, b]插入到

《ACM/ICPC 算法训练教程》读书笔记 之 数据结构(线段树详解)

依然延续第一篇读书笔记,这一篇是基于<ACM/ICPC 算法训练教程>上关于线段树的讲解的总结和修改(这本书在线段树这里Error非常多),但是总体来说这本书关于具体算法的讲解和案例都是不错的. 线段树简介 这是一种二叉搜索树,类似于区间树,是一种描述线段的树形数据结构,也是ACMer必学的一种数据结构,主要用于查询对一段数据的处理和存储查询,对时间度的优化也是较为明显的,优化后的时间复杂为O(logN).此外,线段树还可以拓展为点树,ZWK线段树等等,与此类似的还有树状数组等等. 例如:要将

nyoj 119士兵杀敌(三)(线段树区间最值查询,RMQ算法)

题目119 题目信息 执行结果 本题排行 讨论区 士兵杀敌(三) 时间限制:2000 ms  |  内存限制:65535 KB 难度:5 描写叙述 南将军统率着N个士兵,士兵分别编号为1~N,南将军常常爱拿某一段编号内杀敌数最高的人与杀敌数最低的人进行比較,计算出两个人的杀敌数差值.用这样的方法一方面能鼓励杀敌数高的人,还有一方面也算是批评杀敌数低的人,起到了非常好的效果. 所以,南将军常常问军师小工第i号士兵到第j号士兵中,杀敌数最高的人与杀敌数最低的人之间军功差值是多少. 如今,请你写一个程

经典算法题每日演练——第十二题 线段树

原文:经典算法题每日演练--第十二题 线段树 这一篇我们来看树状数组的加强版线段树,树状数组能玩的线段树一样可以玩,而且能玩的更好,他们在区间求和,最大,平均 等经典的RMQ问题上有着对数时间的优越表现. 一:线段树 线段树又称"区间树”,在每个节点上保存一个区间,当然区间的划分采用折半的思想,叶子节点只保存一个值,也叫单元节点,所 以最终的构造就是一个平衡的二叉树,拥有CURD的O(lgN)的时间. 从图中我们可以清楚的看到[0-10]被划分成线段的在树中的分布情况,针对区间[0-N],最多有

算法模板——线段树之Lazy标记

一.前言 前面我们已经知道线段树能够进行单点修改和区间查询操作(基本线段树).那么如果需要修改的是一个区间该怎么办呢?如果是暴力修改到叶子节点,复杂度即为\(O(nlog_n)\),显然是十分不优秀的.那么我们能不能向区间查询一样把复杂度降到\(O(log_n)\)呢? 二.算法流程 线段树肯定是兹瓷\(O(log_n)\)修改的,否则发明它有何用处?所以,我我们现在需要知道,如何快速进行区间修改操作.首先,我们回顾下终止节点 假定我要在这个图上修改区间[2,8],我只要修改掉图上所有的终止节点