0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一个数据结构-线段树

算法与数据结构 来源:算法与数据结构 2020-05-06 11:02 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

一、概念解析

这次来介绍一个数据结构 - 线段树。

在平时刷题或是工作中,经常会遇到这么一个问题,“给定一个数组,求出数组某段区间的一些性质”。

比如给定一个数组 [5,2,6,1,-4,0,9,2],让你求出区间 [1,4] 上所有元素的和,在这个例子中,答案是 2 + 6 + 1 + (-4) = 5。

你可能会说,直接遍历一遍不就好了吗?

最简单的方式就是直接遍历一遍区间,时间复杂度也显而易见 O(n),如果在这个数组上频繁进行这个操作,那么效率相对来说会比较低,怎么优化呢?

对于求区间和的问题,前缀和数组是一个不错的选择,构建好前缀和数组后,求一个区间和的话只要前后一减就可以了,如果不算构建数组的时间,那么每次的操作时间复杂度就是 O(1)。

这里的问题在于前缀和数组只能解决求区间和的问题,但是其他的区间问题,前缀和数组并不能很好的解决,比如求某段区间上的最大值。

因此我们需要一个数据结构能够帮助我们解决大部分数组的区间问题,而且时间复杂度要尽可能的低。

这也就是今天的主题 - 线段树,首先要说明一点的是,线段树也是二叉树,只是它的节点里面含有区间的信息。

线段树每个节点表示的是一个区间,每个节点将其表示的区间一分为二,左边分到左子树,右边分到右子树,根节点表示的是整个区间(也就是整个数组),叶子节点表示的是一个 index(也就是单个元素),因为每次对半分的缘故,线段树构建出来是平衡的,也就是说树的高度是 O(logn),这里的 n 表示的是数组中所有的元素,这一点对于我们后面分析复杂度很重要。

线段树有三个基本的操作,分别是构建线段树(build)、区间查找(query)、还有就是修改(modify),假设我们现在需要解决的问题是 “求区间上的最大值”,例子还是之前的例子,一起来看看怎么实现这些操作。

对于构建操作来说,相对简单,你只需要记住 “自上而下而下递归分裂,自下而上回溯更新” 从根节点到叶子节点我们不断地将区间一分为二,从叶子节点开始返回值,一直到根节点,不断地更新区间信息。

查找操作是线段树的核心操作,考虑的情况相对较多,这里有四种情况:

情况一:节点区间包含查找区间。这种情况直接递归向下查找即可

情况二:节点区间不相交于查找区间。因为没有要查找的范围,停止搜索

情况三:节点区间相交但不包含查找区间。将区间分成两段,分别查找

情况四:节点区间相等于查找区间。直接返回答案

说明一下,这里说的 “包含” 的意思是一个区间全部元素都被另外一个区间涵盖,“相交” 的意思是一个区间的部分元素被另外一个区间涵盖,例如要查找的区间是 [1,3],那么 [0,4] 包含要查找的区间,[2,5] 只是相交要查找的区间。对于区间查找,后面有图解,跟着例子走一遍印象会更深刻。

最后一个修改操作,和构建操作类似,但有些许不同,你只需要记住 “自上而下递归查找,自下而上回溯更新”。修改的意思是,修改数组中的一个元素的值,这会影响相关的区间,相关的树节点,因此,相关联的节点也就需要更新。

线段树灵活的地方在于,树节点中存放的数据不同,它的功能就不同,比如说,你想要求解区间和,那么树节点中就存放对应区间元素的和,你想求解区间上的最大值,那么树节点中存放的就是对应区间上的最大值。不过话说回来,线段树并不是一个被广泛应用的数据结构,原因可能在于线段树的构建和使用相对于前缀和数组这样的技巧来说,稍微复杂了些。但是在解决数组区间的问题上,线段树可以提供一个还不错的思考方向。

二、动画描述

三、代码实现

publicclassSolution{ privateclassSegmentTreeNode{ intstart,end,max; SegmentTreeNodeleft,right; SegmentTreeNode(intstart,intend,intmax){ this.start=start; this.end=end; this.max=max; this.left=this.right=null; } } publicSegmentTreeNodebuild(intstart,intend,int[]nums){ if(start>end){ returnnull; } if(start==end){ returnnewSegmentTreeNode(start,end,nums[start]); } SegmentTreeNodenode=newSegmentTreeNode(start,end,nums[start]); //自上而下而下递归分裂 if(start!=end){ intmid=(start+end)/2; node.left=build(start,mid,nums); node.right=build(mid+1,end,nums); } //自下而上回溯更新 if(node.left!=null&&node.left.max>node.max){ node.max=node.left.max; } if(node.right!=null&&node.right.max>node.max){ node.max=node.right.max; } returnnode.max; } publicintquery(SegmentTreeNoderoot,intstart,intend){ //如果节点区间相等于查找区间,直接返回对应的值即可 if(root.start==start&&root.end==end){ returnroot.max; } intmid=(root.start+root.end)/2; intleftMax=Integer.MIN_VALUE,rightMax=Integer.MIN_VALUE; //判断是否需要去左子树查找 if(start<= mid) {             // 节点相交查找区间的情况             if (end >mid){ leftMax=query(root.left,start,mid); }//节点包含查找区间的情况 else{ leftMax=query(root.left,start,end); } } //判断是否需要去右子树查找 if(mid< end) {             // 节点相交查找区间的情况             if (start <= mid) {                 rightMax = query(root.right, mid + 1, end);             } // 节点包含查找区间的情况             else {                 rightMax = query(root.right, start, end);             }         }         return Math.max(leftMax, rightMax);     }     public void modify(SegmentTreeNode root, int index, int value) {         // 找到对应的叶子节点,进行元素更新         if (index == root.start && index == root.end) {             root.max = value;         }         if (root.start >=root.end){ return; } //自上而下递归查找 modify(root.left,index,value); modify(root.right,index,value); //自下而上回溯更新 root.max=Math.max(root.left.max,root.right.max); } }

四、复杂度分析

三个操作中,构建树的操作的时间复杂度是 O(n),原因也很好解释,构建的树有 2n 个节点,你可能会问这个 2n 是怎么得到的,思考这个问题可以从叶子节点出发,一共有 n 个叶子节点,构建操作是从上往下不断二分,这样保证了树的平衡,因此所有节点个数就是 n + n/2 + n/4 + ... + 1 = 2n。

由于构建每个节点只花了 O(1) 的时间,因此整个构建的时间复杂度就是 O(2n),忽略常数项,也就是 O(n)。

修改和查找都是沿着一条或者几条从上到下的路径进行的,因为树的高度是 O(logn),所以这两个操作的时间复杂度也是 O(logn)。可以看到这个时间复杂度比暴力的 O(n) 还是快不少。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 数据结构
    +关注

    关注

    3

    文章

    573

    浏览量

    41675
  • 二叉树
    +关注

    关注

    0

    文章

    74

    浏览量

    12999
  • 数组
    +关注

    关注

    1

    文章

    420

    浏览量

    27465

原文标题:什么是线段树?

文章出处:【微信号:TheAlgorithm,微信公众号:算法与数据结构】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    C#运动控制开源(): CAD导图和小线段速度前瞻的优化之CAD导图

    C#实现CAD解析及如何对小线段轨迹进行运动前瞻
    的头像 发表于 04-02 14:33 1153次阅读
    C#运动控制开源(<b class='flag-5'>一</b>): CAD导图和小<b class='flag-5'>线段</b>速度前瞻的优化之CAD导图

    无线倾角传感器在古监测中的应用:以科技守护活文物的结构安全

    无线倾角传感器在古监测中的应用:以科技守护活文物的结构安全
    的头像 发表于 01-09 11:38 769次阅读
    无线倾角传感器在古<b class='flag-5'>树</b>监测中的应用:以科技守护活文物的<b class='flag-5'>结构</b>安全

    10例子代码,C语言结构体的高级

    ;, current->data); current = current->next; } return 0; } 这个例子展示了结构体的自引用,其中每个结构体节点包含
    发表于 01-05 06:32

    每次用串口调试助手发送01之后,就会都到6个数据,为什么?

    单片机通讯,发送01点亮灯,再次发送熄灭。程序中不需要单片机发送数据给电脑,可是每次用串口调试助手发送01之后,就会都到6个数据,不知道为什么,请高手指点。晶振是11.0592,波
    发表于 12-15 06:52

    实现嵌入式的软件定时器

    旦开始运行,tickCnt将不停地加,而每个软件定时器都记录着到期时间,只要tickCnt大于该到期时间,就代表定时器到期了。 3.2 数据结构 软件定时器的
    发表于 12-10 08:29

    typedef结构体使用

    虽然结构体的出现能够让我们有更科学的数据结构来管理数据,但是每次使用结构体都需要struct
    发表于 12-08 07:04

    C语言程序的结构

    )(void); //处理程序   uInt8 ms_count; //时间片大小   } _op_;   数据结构定义好之后,接着就是实现代码,包括三部分,即初始化数据、时间片的刷新与时间到执行
    发表于 11-26 08:12

    Verilog实现使用Booth编码和Wallace的定点补码乘法器原理

    莱士中,对于16位乘法,其每位至少包含6全加器。对于32位乘法,全加器个数则为14。这样子做可以保证对于首位,其有6/14空闲的进位要求,这至少可满足n-2
    发表于 10-23 08:01

    关于E203内核高性能乘法器优化(

    乘法器和阵列乘法器都要困难,且资源消耗比迭代乘法器和阵列乘法器要多 2.4Wallace乘法器 线性阵列乘法器结构简单实现起来较为容易,但每级只有
    发表于 10-23 06:09

    蓝牙网关连接个数是多少

    我司蓝牙网关标准版本是8连接个数,多连接版本是19的连接个数。蓝牙网关的“同时连接数”与信号质量呈“此消彼长”关系:连接数越多,留给每个从设备的时隙、带宽和缓存越少,导致丢包率上升
    的头像 发表于 10-11 16:02 873次阅读
    蓝牙网关连接<b class='flag-5'>个数</b>是多少

    分享嵌入式学习阶段规划

    给大家分享嵌入式学习阶段规划: ()基础筑牢阶段(约 23 天) 核心目标:打牢 C 语言、数据结构、电路基础C 语言开发:学变量 / 指针 /
    发表于 09-12 15:11

    【HZ-T536开发板免费体验】6、使用protoc-gen-gorm生成标准化的数据结构

    在设计espnow协议的时候,考虑到我需要在esp32,Linux设备,web上使用相同的数据结构,那就需要考虑下,是否使用通用的跨平台序列化
    发表于 08-26 00:32

    科技预告新款人形机器人:有31关节

    机器人似乎要搞大事了,宇科技发布了新款人形机器人的海报,虽然配文只有“敬请期待”几个字,但是根据海报信息显示新款机器人配备有31关节(海报显示是6*2+3+7*2+2)。而且是
    的头像 发表于 08-19 23:10 2173次阅读

    程序设计与数据结构

    《程序设计与数据结构》重点阐述了三大方向内容: 1. C语言学习中的痛点:针对当前工程师在C语言学习中的痛点,如指针函数与函数指针,如何灵活应用结构体等。从变量的三要素(变量的类型,变量的值和变量
    发表于 05-13 16:45

    请问K230D怎么将摄像头采集的视频数据通过串口输出?

    我连了WiFi模块,想要将摄像头采集的视频数据通过串口发送出去。之前都是用的STM32,不太会MicroPython,搞不懂对象的数据结构,求教。
    发表于 04-28 06:16