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

    文章

    557

    浏览量

    39869
  • 二叉树
    +关注

    关注

    0

    文章

    74

    浏览量

    12210
  • 数组
    +关注

    关注

    1

    文章

    406

    浏览量

    25580

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

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

收藏 人收藏

    评论

    相关推荐

    C语言数据结构之跳表详解

    大家好,今天分享一篇C语言数据结构相关的文章--跳表。
    的头像 发表于 12-29 09:32 487次阅读
    C语言<b class='flag-5'>数据结构</b>之跳表详解

    redis数据结构的底层实现

    Redis是一种内存键值数据库,常用于缓存、消息队列、实时数据分析等场景。它的高性能得益于其精心设计的数据结构和底层实现。本文将详细介绍Redis常用的数据结构和它们的底层实现。 Re
    的头像 发表于 12-05 10:14 283次阅读

    不同数据结构的定义代码

    数据结构是相互之间存在一种或多种特定关系的数据元素的集合。
    的头像 发表于 11-29 14:13 314次阅读

    ringbuffer数据结构介绍

    最近在研究srsLTE的代码,其中就发现一个有意思的数据结构------ringbuffer。 虽然,这是一个很基本的数据结构,但时,它在LTE这种通信协议栈系统中却大行其道,也是很容易被协议
    的头像 发表于 11-13 10:44 268次阅读
    ringbuffer<b class='flag-5'>数据结构</b>介绍

    epoll的基础数据结构

    先看一下 eventpoll 这个数据结构,这个数据结构是我们在调用 epoll_create 之后内核创建的一个句柄,表示了一个 epoll 实例。后续如果我们再调用 epoll_ctl
    的头像 发表于 11-10 10:20 276次阅读
    epoll的基础<b class='flag-5'>数据结构</b>

    NetApp的数据结构是如何演变的

    混合和多云部署模型是企业IT组织的新常态。随着这些复杂的环境,围绕数据管理的新挑战出现了。NetApp的数据管理愿景是一种无缝连接不同的数据结构云,无论它们是私有环境、公共环境还是混合环境。
    发表于 08-25 17:15 0次下载
    NetApp的<b class='flag-5'>数据结构</b>是如何演变的

    I2C设备驱动的两个数据结构

    设备驱动 I2C 设备驱动重点关注两个数据结构:i2c_client 和 i2c_driver。i2c_client 就是描述设备信息的,i2c_driver 描述驱动内容。 一个设备对应
    的头像 发表于 07-22 15:49 401次阅读
    I2C设备驱动的两<b class='flag-5'>个数据结构</b>

    Linux平台下生成C语言数据结构关系图

    作为一名Linux系统下的C语言开发,经常需要阅读源码,但是有些源码实在是难以阅读,各种庞大的结构体交杂,分分钟把你绕晕,让你头昏眼花,迟迟无法梳理清楚。这时候,一个能够帮你梳理数据结构的工具就显得极其重要,让你能够很清晰的看出各个数据
    发表于 07-14 16:46 300次阅读
    Linux平台下生成C语言<b class='flag-5'>数据结构</b>关系图

    快速介绍8种常用数据结构

    数据结构是一种特殊的组织和存储数据的方式,可以使我们可以更高效地对存储的数据执行操作。数据结构在计算机科学和软件工程领域具有广泛而多样的用途。
    发表于 06-21 09:27 526次阅读
    快速介绍8种常用<b class='flag-5'>数据结构</b>

    如何理解并掌握Java数据结构

    Java 数据结构是 Java 程序员必须掌握的重要知识之一。
    的头像 发表于 06-06 15:53 555次阅读

    数据结构解决滑动窗口问题

    前文用 [单调栈解决三道算法问题]介绍了单调栈这种特殊数据结构,本文写一个类似的数据结构「单调队列」。 也许这种数据结构的名字你没听过,其实没啥难的,就是一个「队列」,只是使用了一点巧妙的方法,使得 队列中的元素全都是单调递增
    的头像 发表于 04-19 10:50 430次阅读
    <b class='flag-5'>数据结构</b>解决滑动窗口问题

    常见的数据结构有哪些

    数据结构是计算机存储、组织数据的方式,是指相互之间存在一种或多种特定关系的数据元素的集合
    的头像 发表于 04-06 17:26 2152次阅读
    常见的<b class='flag-5'>数据结构</b>有哪些

    算法和数据结构基础知识分享(下)

    有哪些常见的数据结构?基本操作是什么?常见的排序算法是如何实现的?各有什么优缺点?本文简要分享算法基础、常见的数据结构以及排序算法。
    的头像 发表于 04-06 16:48 530次阅读
    算法和<b class='flag-5'>数据结构</b>基础知识分享(下)

    算法和数据结构基础知识分享(中)

    有哪些常见的数据结构?基本操作是什么?常见的排序算法是如何实现的?各有什么优缺点?本文简要分享算法基础、常见的数据结构以及排序算法。
    的头像 发表于 04-06 16:48 373次阅读
    算法和<b class='flag-5'>数据结构</b>基础知识分享(中)

    算法和数据结构基础知识分享(上)

    有哪些常见的数据结构?基本操作是什么?常见的排序算法是如何实现的?各有什么优缺点?本文简要分享算法基础、常见的数据结构以及排序算法。
    的头像 发表于 04-06 16:48 556次阅读
    算法和<b class='flag-5'>数据结构</b>基础知识分享(上)