`

线段树

阅读更多

把问题简化一下:

在自然数,且所有的数不大于30000的范围内讨论一个问题:现在已知n条线段,把端点依次输入告诉你,然后有m个询问,每个询问输入一个点,要求这个点在多少条线段上出现过;

最基本的解法当然就是读一个点,就把所有线段比一下,看看在不在线段中;

每次询问都要把n条线段查一次,那么m次询问,就要运算m*n次,复杂度就是O(m*n)

这道题m和n都是30000,那么计算量达到了10^9;而计算机1秒的计算量大约是10^8的数量级,所以这种方法无论怎么优化都是超时

-----

因为n条线段是固定的,所以某种程度上说每次都把n条线段查一遍有大量的重复和浪费;

线段树就是可以解决这类问题的数据结构

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

在[0,7]区间上建立一棵满二叉树:(为了和已知线段区别,用【】表示线段树中的线段)

                                               【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】

每个节点用结构体:

struct line
{
      int left,right;//左端点、右端点
      int n;//记录这条线段出现了多少次,默认为0
}a[16];

和堆类似,满二叉树的性质决定a[i]的左儿子是a[2*i]、右儿子是a[2*i+1];

然后对于已知的线段依次进行插入操作:

从树根开始调用递归函数insert

void insert(int s,int t,int step)//要插入的线段的左端点和右端点、以及当前线段树中的某条线段
{
      if (s==a[step].left && t==a[step].right)
      {
            a[step].n++;//插入的线段匹配则此条线段的记录+1
            return;//插入结束返回
      }
      if (a[step].left==a[step].right)   return;//当前线段树的线段没有儿子,插入结束返回
      int mid=(a[step].left+a[step].right)/2;
      if (mid>=t)    insert(s,t,step*2);//如果中点在t的右边,则应该插入到左儿子
      else if (mid<s)    insert(s,t,step*2+1);//如果中点在s的左边,则应该插入到右儿子
      else//否则,中点一定在s和t之间,把待插线段分成两半分别插到左右儿子里面
      {
            insert(s,mid,step*2);
            insert(mid+1,t,step*2+1);
      }
}

三条已知线段插入过程:

[2,5]

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

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

--[2,3]与【2,3】匹配,【2,3】记录+1;[4,5]与【4,5】匹配,【4,5】记录+1

[4,6]

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

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

--[4,5]与【4,5】匹配,【4,5】记录+1;[6,6]与【6,7】比较,插到左儿子【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

建树有n次插入操作,n*logN,一次查询要logN,m次就是m*logN;总共复杂度O(n+m)*logN,这道题N不超过30000,logN约等于14,所以计算量在10^5~10^6之间,比普通方法快了1000倍;

这道题是线段树最基本的操作,只用到了插入和查找;删除操作和插入类似,扩展功能的还有测度、连续段数等等,在N数据范围很大的时候,依然可以用离散化的方法建树。

原文见:http://hi.baidu.com/alpc62/blog/item/469edeca0043e382c8176875.html/cmtid/64896338f14e612db9998f6e

分享到:
评论
1 楼 fuliang 2009-03-24  
补充一下建树操作:
void create(int pos,int left,int right){    
    a[pos].left = left;   
    a[pos].right = right;   
    if(a[pos].left < a[pos].right){   
       int mid = (left + right) >> 2;   
       create(pos * 2, left,mid);   
       create(pos * 2 + 1, mid + 1,right);   
    }   
} 

相关推荐

    线段树的一种实现

    线段树是一种高效的数据结构,常用于处理区间查询与更新问题。它的核心思想是将一个一维数组通过分治策略转化为一棵二叉树,每个节点代表一个区间的值,这棵树通常具有平衡性质,比如平衡二叉搜索树的特性。线段树在...

    pascal区间线段树

    **Pascal 区间线段树详解** 线段树(Segment Tree)是一种数据结构,用于高效地处理数组或序列上的区间查询与修改操作。在Pascal编程语言中,我们可以使用记录(Record)类型来实现一个区间线段树。以下是基于给定...

    线段树,数据结构的选择与算法效率

    ### 线段树:数据结构的选择与算法效率 #### 引言 在程序设计领域,数据结构的选择和算法的设计是解决复杂问题的关键。合理的数据结构不仅能够简化问题的复杂度,还能显著提高算法的执行效率。《线段树,数据结构...

    acm程序设计竞赛_培训_线段树

    浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计竞赛_培训_线段树浙江大学_acm程序设计...

    几道经典线段树题目及代码

    线段树,作为一种高效的数据结构,广泛应用于计算机科学和算法设计中,特别是在处理区间查询和更新问题时。它能够以O(logN)的时间复杂度完成区间内的查询与修改操作,大大提高了程序的运行效率。本资源包含了一些...

    线段树完整版

    ### 线段树知识点详解 #### 一、线段树基本概念与应用 线段树是一种二叉树数据结构,常用于处理区间查询或区间更新的问题。它将一个线性序列分割成多个不连续的段,每一个节点代表一个区间,并且通过递归的方式将...

    线段树六题

    线段树是一种非常有效的数据结构,主要用于解决区间查询和更新的问题,常用于算法竞赛和实际应用中的问题求解。接下来,我们将详细探讨“线段树六题”中各个题目的相关知识点。 ### 1. PKU2352 star2 校门外的树 **...

    线段树 二分 统计 ACM

    线段树是一种数据结构,主要用于高效地处理区间查询与修改问题。在计算机科学,特别是算法竞赛(如ACM/ICPC)中,线段树是一个非常重要的工具,它能够帮助我们快速解决涉及区间操作的问题,比如求区间和、查找区间...

    线段树.pdf

    线段树是一种高效的二叉树形数据结构,用于存储区间或线段,并允许快速查询其构成的区间内的信息。它可以用于解决诸如区间求和、区间最值等区间查询问题,并支持单点更新和成段更新等操作。 首先,线段树的实现基础...

    线段树高级数据结构实现

    ### 线段树高级数据结构实现:点更新 #### 一、线段树简介 线段树是一种高效的数据结构,常用于处理区间查询或区间更新的问题。它将一个线性序列分割成多个部分,每个节点代表一个区间的统计信息(如最大值、...

    线段树——在ACM中制胜的法宝

    线段树——在ACM中制胜的法宝 线段树是一种非常重要的数据结构,广泛应用于算法竞赛(ACM)和实际编程中。线段树可以高效地解决许多问题,如区间查找、区间更新、区间统计等。但是,对于线段树的理解和应用,需要对...

    acm线段树基本

    ACM线段树基本 线段树是一种常用的数据结构,广泛应用于算法竞赛编程和数据结构设计中。它是一种树形结构,能够高效地进行区间查询和修改操作。 线段树的基本概念 线段树是一棵二叉树,每个结点表示一个区间。...

    剖析线段树与矩形切割

    线段树和矩形切割是数据结构和算法领域中的重要工具,主要用于处理动态查询和更新问题,以及在二维空间中的几何问题。这两种方法在解决复杂问题时展现了强大的灵活性和高效性,尤其在处理大规模数据时。 线段树是一...

    线段树PPT两个,所有常规用法

    线段树是一种高效的数据结构,常用于处理区间查询与修改问题。在计算机科学特别是算法竞赛(ACM)中,线段树是解决动态区间问题的重要工具。本篇将详细讲解线段树的基本概念、构建方法、操作类型以及常见应用。 ...

    完全版线段树

    线段树是一种高效的数据结构,常用于处理动态的区间查询和修改问题。在这个“完全版线段树”中,作者NotOnlySuccess分享了他对线段树的理解和优化,旨在为ACMer(编程竞赛爱好者)提供更好的学习资源。下面将详细...

    线段树学习步骤

    线段树是一种高效的数据结构,常用于处理区间查询和区间更新的问题。它的核心思想是将一个数组分成多个连续的子区间,并对每个子区间维护一个代表性的信息,从而实现快速查询和更新。以下是对线段树学习步骤的详细...

    线段树及其应用(PPT)

    线段树是一种高效的数据结构,尤其适用于处理区间查询与更新问题。它在信息学、数据结构和OI(奥林匹克信息学)领域中具有广泛的应用。线段树的核心思想是通过二分的思想将一个大区间划分为若干小的子区间,每个节点...

    线段树 数据结构 数 统计 RMQ

    线段树是一种高效的数据结构,主要用于处理区间查询和更新操作,尤其在解决区间最小值、最大值、求和等统计问题以及动态维护区间状态时表现出色。本文将深入解析线段树的基本概念、构建原理、关键操作以及在RMQ...

    几道经典线段树题目及代码2

    线段树是一种数据结构,主要用于高效地处理区间查询与区间更新问题。在计算机科学和算法竞赛中,线段树是解决动态区间问题的利器。它能够以近似于log n的时间复杂度完成对一个n元素数组的区间操作,其中n为数组大小...

Global site tag (gtag.js) - Google Analytics