史上最详细的线段树教程

线段树是一种高效的高级数据结构,用于解决区间统计问题,如求最值、区间和等。本文详细介绍了线段树的构建、区间查询、单点更新和区间更新,以及延迟更新的概念,旨在帮助读者理解线段树的工作原理和应用。
摘要由CSDN通过智能技术生成

线段树是一种树形的高级数据结构,因为noip不考这种数据结构,而且当时的代码实现能力比较差,编程水平比较低,所以高中的时候粗略的看了看,并没有真正的搞懂,虽然早已弃了算法竞赛的坑,但还是回过头去研究了一下这种高级数据结构并写下此教程,以纪念曾经奋斗在OI道路上的日子。

问题提出:

现有如下的问题,给定一个的序列,实现以下操作:

①更新序列的某个值。

②查询序列的某个区间的最小值(最大值、区间和)线段树常用于解决区间统计问题。求最值,区间和等操作均可使用该数据结构,本篇以求最小值为例。

③更新序列的某个区间内的所有值。

对于求最小值,我们很容易想到的算法就是。更新序列的某个值直接找到该值,更新,时间复杂度是O(1);区间查询直接遍历该区间,时间复杂度是O(n);区间修改的也是直接遍历该区间修改,时间复杂度是O(n),在数据量特别大,操作比较多的时候,效率是很低的。另一种解法是这样的。构建一个二维数组,a[i][j]表示区间[i,j]的最小值。这样查询操作的复杂度为O(1),但是这样的话,修改的复杂度也不低而且如果数据量特别大,O(n^2)的空间复杂度也是不容忽视的。这时候就需要我们是用线段树这种优秀的高级数据结构来解决了。

线段树:

我们以序列{5,9,7,4,6,1}为例子演示。这个序列构成的线段树是这样的。

 

 

从这颗树上我们可以了解线段树的这几个特点,线段树是一颗近似的完全二叉树,每个节点代表一个区间,节点的权值是该区间的最小值。根节点是整个区间。每个节点的左孩子是该节点所代表的的区间的左半部分,右孩子是右半部分。为方便起见,如果区间长度为奇数,则左孩子为较长的半部分。通过线段树,我们可以用O(logn)的时间复杂度完成查询和更新操作。当然,预处理的时间复杂度是O(n)。

线段树的构建:

我们将每一个节点封装到Node类中。

 

class Node//节点
{
    int start;//区间的左端点
    int end;//区间的右端点
    int data;//该节点的值
    int mark = 0;//延迟更新的标记
    public Node(int start,int end)//构造方法中传入左端点和右端点
    {
        this.start = start;
        this.end = end;
    }
    void addMark(int value)//做标记
    {
        this.mark+=value;
    }
    void clearMark()
    {
        this.mark = 0;
    }
    public String toString()
    {
        return start+"-"+end;
    }
}

这个节点类有四个域,分别是区间左端点,区间右端点,节点的权值,延迟更新的标记(暂时用不到)构造方法中传入左右端点。构建线段树的时候,我们可以从根开始构建,我们注意到,这颗线段树的叶子节点区间中的元素只有一个,代表着序列的元素,所以n个元素的序列构成的线段树中的叶子节点有n个。这颗线段树虽然不是完全二叉树,但是可以通过移动变成一颗完全二叉树。n个叶子节点的完全二叉树的非叶子结点个数为n-1所以n个元素的序列构成的线段树的节点有2n-1个。这2n-1个只是有效的节点,因为我们用数组存储,所以需要给这颗近似的完全二叉树添加一些虚节点,

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值