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

Posted 萝卜er

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树 数据结构的简介和 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);
    }
}

 

以上是关于线段树 数据结构的简介和 leetcode 307的主要内容,如果未能解决你的问题,请参考以下文章

[Leetcode]307. Range Sum Query - Mutable

LeetCode1310. 子数组异或查询 / 307. 区域和检索 - 数组可修改(线段树树状数组)

RMQ类问题利器:线段树

RMQ类问题利器:线段树

LeetCode 307. 区域和检索 - 数组可修改

[307]. 区域和检索 - 数组可修改