线段树 数据结构的简介和 leetcode 307

之前一直听说线段树是一个很高级很难的数据结构,今天简单了解了下, 感觉就是二叉树加几个全局变量啊,原来这么easy?(开个玩笑)

简单说几个特点,

1. 每个节点除了存放left,right指针之外,还存着一个范围(这个范围一般是构建线段树之前数组的索引范围), 就是以当前节点为根的情况下,对自己下面所有节点的求交集, 还可以根据你的需求 加一些别的特殊字段,sum,max,min等等

2. 数据集都存在叶子节点上,非叶子节点只做归纳总结

一般还有几个操作

1. 初始化,就是把一个数组初始化成一个线段树(关键是修改你需求中的特殊字段 sum等)

2. 修改某一个索引处的数值,同时保证他的祖先节点的特殊字段是对的

3. 求某一个索引范围内的信息

整体思路是分治,递归求解。

leetcode 307 是一个典型的线段树

给定一个整数数组  nums,求出数组从索引 到 j  (i ≤ j) 范围内元素的总和,包含 i,  j 两点。

update(i, val) 函数可以通过将下标为 的数值更新为 val,从而对数列进行修改。

示例:

Given nums = [1, 3, 5]

sumRange(0, 2) -> 9
update(1, 2)
sumRange(0, 2) -> 8

说明:

  1. 数组仅可以在 update 函数下进行修改。
  2. 你可以假设 update 函数与 sumRange 函数的调用次数是均匀分布的。

这道题,看到题之后,最朴素的解法就是每次都从i到j遍历,更新就直接更新。 这样更新的时间复杂度是o1, 求和的时间复杂度是on

这个时候,思考下有没有别的解决办法, 我们维护一个前n项和的数组,sum(n) = 0 到 n的累加和, range(i,j)= sum(j) - sum(i)

这样的话range的时间复杂度就是o1了, 但是更新的时候你需要更新很多个前n项和,这样更新就成on了, 有没有更优的解法呢?

用segment tree,这样的话更新和查找就都是logn的时间复杂度了,这样就优了不少, 比如n等于1000, 那么一个o1的时间复杂度一个on的时间复杂度那么总共就是1000+

,但是两个都是logn的话,就是20, 如果n比较大的话那么差别就更大了。

talk is cheap, 上代码

class NumArray {
    class SegmentTree {
        int start;
        int end;
        int sum;
        SegmentTree left;
        SegmentTree right;
        SegmentTree(int s, int e, int sum_temp, SegmentTree l, SegmentTree r) {
            start = s;
            end = e;
            sum = sum_temp;
            left = l;
            right = r;
        }
    }
    int[] n;
    SegmentTree head;
    public NumArray(int[] nums) {
        n = nums;
        head = init(nums, 0, nums.length - 1);
    }

    SegmentTree init (int[] nums, int start, int end) {
        if (start > end) return null;
        if (start == end) {
            return new SegmentTree(start, end, nums[start], null, null);
        }
        int mid = (start + end)/2;
        SegmentTree left = init(nums, start, mid);
        SegmentTree right = init(nums, mid + 1, end);
        return new SegmentTree(start, end, left.sum + right.sum, left, right);
    }

    public void update(int i, int val) {
        runUpdate(head, i, val);
    }

    void runUpdate(SegmentTree root, int i, int val) {
        if (root.start == root.end) {
            root.sum = val;
            return;
        }
        int mid = (root.start + root.end)/2;
        if (i <= mid) {
            runUpdate(root.left, i, val);
        } else {
            runUpdate(root.right, i, val);
        }
        root.sum = root.left.sum + root.right.sum;
    }

    public int sumRange(int i, int j) {
        return runSumRange(head, i, j);
    }

    int runSumRange(SegmentTree root, int i, int j) {
        if (root.start == i&&root.end == j) {
            return root.sum;
        }
        int mid = (root.start + root.end)/2;
        if (j <= mid) {
            return runSumRange(root.left, i, j);
        }
        if (i > mid) {
            return runSumRange(root.right, i, j);
        }
        return runSumRange(root.left, i, mid) + runSumRange(root.right, mid + 1, j);
    }
}

原文地址:https://www.cnblogs.com/tobemaster/p/10474420.html

时间: 2024-10-11 23:16:07

线段树 数据结构的简介和 leetcode 307的相关文章

线段树数据结构详解

线段树数据结构详解 Coded by Jelly_Goat. All rights reserved. 这一部分是线段树. 线段树,顾名思义,是一种树形数据结构,适用于各种求区间统一算法的动静两平衡的数据结构. 这里什么是统一算法?(自己口胡的统一算法) 比如求最大值or最小值.区间求和,一样的区间都是一样的算法,这也是和动态dp不同的地方. 前置知识1:二叉搜索树 二叉搜索树就是根节点比左儿子大,比右儿子小的一种二叉树. 前置知识2:向量存储 向量存储是用来存完全二叉树儿子和父亲关系的. 如果

线段树--数据结构专题学习

这两周是数据结构专题的学习,,被专题的题目虐得死去活来== 线段树:简单的说就是把[1,n]的区间二分,[1,(1+n)/2]左子树,[(1+n)/2+1,n]右子树 就这样一直分下去,直到都是[x,x]这样的区间.这样就构成了一颗树了^-^ 有这样一棵树,我们就可以在节点中储存区间的和啊,区间内的最大值啊,最小值等等..这就是线段树的附加信息了,也是题目中的重点.. 我们可以用一个数组(长度为k)储存原区间的初始值,然后根据这个建树,所以这个树的节点数最多为4*K: 对于每个节点i,其左子树为

《ACM/ICPC 算法训练教程》读书笔记 之 数据结构(线段树详解)

依然延续第一篇读书笔记,这一篇是基于<ACM/ICPC 算法训练教程>上关于线段树的讲解的总结和修改(这本书在线段树这里Error非常多),但是总体来说这本书关于具体算法的讲解和案例都是不错的. 线段树简介 这是一种二叉搜索树,类似于区间树,是一种描述线段的树形数据结构,也是ACMer必学的一种数据结构,主要用于查询对一段数据的处理和存储查询,对时间度的优化也是较为明显的,优化后的时间复杂为O(logN).此外,线段树还可以拓展为点树,ZWK线段树等等,与此类似的还有树状数组等等. 例如:要将

【LeetCode】线段树 segment-tree(共9题)+ 树状数组 binary-indexed-tree(共5题)

第一部分---线段树:https://leetcode.com/tag/segment-tree/ p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 12.0px Helvetica } [218]The Skyline Problem [307]Range Sum Query - Mutable [308]Range Sum Query 2D - Mutable [315]Count of Smaller Numbers After Self [493

数据结构--M - 秋实大哥与线段树(单点更新与区间查询)

M - 秋实大哥与线段树 Time Limit: 3000/1000MS (Java/Others)     Memory Limit: 65535/65535KB (Java/Others) Submit Status “学习本无底,前进莫徬徨.” 秋实大哥对一旁玩手机的学弟说道. 秋实大哥是一个爱学习的人,今天他刚刚学习了线段树这个数据结构. 为了检验自己的掌握程度,秋实大哥给自己出了一个题,同时邀请大家一起来作. 秋实大哥的题目要求你维护一个序列,支持两种操作:一种是修改某一个元素的值:一

《数据结构》线段树入门(二)

今天继续介绍——线段树之延迟标记 接上期<数据结构>线段树入门(一):http://www.cnblogs.com/shadowland/p/5870339.html 在上期介绍了线段树的最基本内容(线段树单点修改,区间查询),这次将介绍:区间修改,区间查询. Question: 给你N个数,有两种操作: 1:给区间[a,b]的所有数增加X 2:询问区间[a,b]的数的和. 输入描述: 第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,每行表示操作的个数,如果第一数是1,后接3个正

《数据结构》线段树入门(一)

今天介绍一种非常特殊的数据结构——线段树 首先提出一个问题: 给你n个数,有两种操作: 1:给第i个数的值增加X 2:询问区间[a,b]的总和是什么? 输入描述 输入文件第一行为一个整数n,接下来是n行n个整数,表示格子中原来的整数.接下一个正整数q,再接 下来有q行,表示q个询问,第一个整数表示询问代号,询问代号1表示增加,后面的两个数x和A表示给 位置X上的数值增加A,询问代号2表示区间求和,后面两个整数表示a和b,表示要求[a,b]之间的区间和. 样例输入 4 7 6 3 5 2 1 1

COJ 1010 WZJ的数据结构(十) 线段树区间操作

传送门:http://oj.cnuschool.org.cn/oj/home/problem.htm?problemID=1001 WZJ的数据结构(十) 难度级别:D: 运行时间限制:3000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 请你设计一个数据结构,高效执行以下过程: #include<iostream>using namespace std;const int maxn=100010;int A[maxn];int tp,ql,qr,v;int

COJ 0358 xjr考考你数据结构(根号3)线段树区间修改

xjr考考你数据结构(根号3) 难度级别:C: 运行时间限制:1000ms: 运行空间限制:51200KB: 代码长度限制:2000000B 试题描述 请你编写一个数据结构,完成以下功能: 1)求出第L个到第R个数中的最大.最小值以及连续和. 2)将第addL到addR个数改成v. 输入 第一行:n,表示数的个数第二行:空格分开每个数Ai第三行:Q,表示操作数目后Q行:先输入一个字母,       若字母为“Q”则后面跟上两个数,分别为L与R       若字母为“C”则后面跟上三个数,分别为a