【线段树】 个人对线段树的特点、作用以及实现的理解(从为什么要用到怎么用)

本文介绍了线段树这种数据结构,探讨了在需要频繁查询和更新区间元素时,如何通过线段树提高效率。文章详细阐述了线段树的构建、更新和查询操作,以及pushdown操作的优势,旨在帮助读者理解和掌握线段树的应用。
摘要由CSDN通过智能技术生成

今天刚接触这种数据结构,学完后把自己的思路整理一下,这篇博客就当做总结吧,有错误日后发现了修改。

分界线

为什么要用这种数据结构?

现在,就当我们还没听过这种数据结构,也不知道它的作用是什么,我们来思考以下几个问题。

现在我有一串数组,我要实现下面两个操作:

1.查询:我想知道L–R区间内的元素的和是多少。
2.更新:我要实现更新某一下标对应元素的值。
以上两个步骤会交叉进行多次。

那么,以我们现有的知识,我们会对上面问题进行这样的操作。

1.查询:用循环计算L–R区间内的元素累加和。时间复杂度O(n)。
2.更新:只需要对应arr[idx] = val(要修改的值)即可。时间复杂度O(1)。

我们知道,更新的操作只需要一步,非常快,而查询操作却要线性的时间复杂度,在两种操作并行的时候,查询操作会占去大部分时间。从而影响整体的效率。

那么,怎么把查询操作也变成O(1)的时间复杂度呢?

很简单,大家都知道,以前缀和的形式记录就行啦。

这样查询操作就变成sum[R] - sum[L-1]了,表示的是L到R区间内的元素和。
看起来变成了O(1),整体变快了,但是确实如此吗?
我们再来看看更新:这时候更新某一位置的元素,其后面的位置的前缀和也会跟着发生改变,也就是修改当前一个,要多修改后面n个。

这又变成了O(n)的时间复杂度了!

如图:
在这里插入图片描述

看来,鱼和熊掌不可兼得,不能将两者都维持到一个很低的复杂度上。那么,我想知道有没有一种方法,可以将整体的时间复杂度维持到一个比线性更快的水平上呢?这就切入正题,抛出线段树这一概念,解决的就是查询与更新并行时整体效率的问题
分界线

何为线段树?

首先概念是:
1.线段树是一颗二叉搜索树。
2.每个结点包含着数组对应区间的信息,并必有左右分支。

如何构建这样一颗树?

因为我们查询的时候无非是获取区间内的和,那么我就可以将数的端点用来存储对应区间的和,等到要用的时候找到搜索区间就好了,时间复杂度O(logn)。那么便顺着思路将根节点定义为 [0, a.size() -1 ]内的元素和,左右分支对半分割当前结点区间,左结点记录[ 0,mid ]和,右节点记录[ mid+1, a,size()-1 ]和。对于这两个分支,也是同理,不断分割区间,知道叶结点表示区间只有一个元素时到底
理想的树结构应该是这样(图中结点内数字表示区间)
在这里插入图片描述
我这里以数组的方法建立树结构,那么标上下标就是
在这里插入图片描述
可以看到每次在建立左右分支的时候,其下标分别是当前node2+1和node2+2。
这样,我们就把树建好了。我们看看代码如何实现
详细的操作见代码及注释。

//树的生成
void build_tree(int arr[],int tree[], int node, int start, int end)//node表示当前结点,start and end表示当前结点对应在数组上的区间。
{
   
        if(start==end) //递归出口,当对应区间只含有一个元素时,返回当前元素
        {
   
            tree[node] = arr[start];
            return;
        }

        int mid = (start+end)/2;
        int left_node   =  2*node + 1;  //左结点下标
        int right_node = 2*node + 2;//右节点下标

        build_tree(arr,tree,left_node,start,mid);//建立左树
        build_tree(arr,tree,right_node,mid+1,end);  //建立右树</
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值