美文网首页数据结构Go数据结构
(13)Go实现线段树-数组实现

(13)Go实现线段树-数组实现

作者: 哥斯拉啊啊啊哦 | 来源:发表于2019-04-23 08:58 被阅读1次

线段树,也叫区间树,segmengt-tree,是一种长度不变的平衡树结构,父节点存储的结果是左右子节点的总计。
以数组arrs求和为例子:
1)每个父节点存储的都是所有子节点的总和
2)所有叶子节点均为arrs的单个元素值
如下图


问:数组arrs有n个元素,依此创建的线段树需要由多少个节点?
//
答:需要4n的空间,解答如下图

使用线段树时,不考虑添加元素,一般采用4n的静态空间即可
问:为什么要使用线段树?
答:在区间统计,区间染色这类区间不变的问题中,线段树更好高快速的解决问题。
    在基于线段树的更新和查询中,时间复杂度都能达到O(logn)
// TODO: 基于数组实现的线段树
type segmentTree struct {
    tree   []int    //线段树
    data   []int    //数组数据
    merger func(v1, v2 int) int    //线段树功能函数,如求和,求余等等
}

func leftChild(i int) int {
    return 2*i + 1
}

// 传入一个数组arrs和一个功能函数func,根据功能函数返回一个线段树
func NewSegmentTree(arrs []int, merger func(i1, i2 int) int) *segmentTree {
    length := len(arrs)

    tree := &segmentTree{
        tree:   make([]int, length*4),
        data:   arrs,
        merger: merger,
    }
    tree.bulidSegmentTree(0, 0, length-1)

    return tree
}

// 在tree的index位置创建 arrs [ l 到 r ]  的线段树
func (tree *segmentTree) bulidSegmentTree(index, l, r int) int {
    if l == r {
        tree.tree[index] = tree.data[l]
        return tree.data[l]
    }

    leftI := leftChild(index)
    rightI := leftI + 1
    mid := l + (r-l)/2
    leftResp := tree.bulidSegmentTree(leftI, l, mid)
    rightResp := tree.bulidSegmentTree(rightI, mid+1, r)

    tree.tree[index] = tree.merger(leftResp, rightResp)
    return tree.tree[index]
}

// 查询arrs范围queryL到queryR 的结果
func (tree *segmentTree) Query(queryL, queryR int) (int, error) {
    length := len(tree.data)
    if queryL < 0 || queryL > queryR || queryR >= length {
        return 0, errors.New(
            "index  is illegal ")
    }
    return tree.queryrange(0, 0, length-1, queryL, queryR), nil
}

// 在以index为根的线段树中[l...r]范围里,搜索区间[queryL...queryR]的值
func (tree *segmentTree) queryrange(index, l, r, queryL, queryR int) int {
    if l== queryL && r== queryR {
        return tree.tree[index]
    }

    leftI := leftChild(index)
    rightI := leftI + 1
    mid := l+ (r-l)/2

    if queryL > mid {
        return tree.queryrange(rightI, mid+1, r, queryL, queryR)
    }
    if queryR <= mid {
        return tree.queryrange(leftI, l, mid, queryL, queryR)
    }

    leftResp := tree.queryrange(leftI, l, mid, queryL, mid)
    rightResp := tree.queryrange(rightI, mid+1, r, mid+1, queryR)
    return tree.merger(leftResp, rightResp)
}

// 更新data中索引k的值为v
func (tree *segmentTree) Update(k, v int) {
    length := len(tree.data)
    if k < 0 || k >= length {
        return
    }
    tree.set(0, 0, length-1, k, v)
}

// 在以treeIndex为根的线段树中更新index的值为e
func (tree *segmentTree) set(treeIndex, l, r, k, v int) {
    if l == r {
        tree.tree[treeIndex] = v
        return
    }

    leftI := leftChild(treeIndex)
    rightI := leftI + 1
    midI := l + (r-l)/2

    if k > midI {
        tree.set(rightI, midI+1, r, k, v)
    } else {
        tree.set(leftI, l, midI, k, v)
    }

    tree.tree[treeIndex] = tree.merger(tree.tree[leftI], tree.tree[rightI])
}

func (tree *segmentTree) Print() {
    fmt.Println(tree.tree)
}
测试线段树
func multiplication(v1, v2 int) int {
    return v1 * v2
}

func main() {
    c := []int{-1, 1, 2, -3, 4, 5, 6}

    a := segment_tree1.NewSegmentTree(c, multiplication)
    a.Print()

    resp, err := a.Query(2, 5) //-120
    fmt.Printf("查询结果:%d,  错误:%v\n", resp, err)

    a.Update(2, -2)
    a.Print()
}
测试结果
[720 6 120 -1 -6 20 6 -1 1 2 -3 4 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]
查询结果:-120,  错误:<nil>
[-720 -6 120 -1 6 20 6 -1 1 -2 -3 4 5 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]

相关:
用线段树解决leetcode-307题目:区间索引和检索
https://www.jianshu.com/p/fc53b9ff0a80

有bug欢迎指出,转载请注明出处。

相关文章

  • (13)Go实现线段树-数组实现

    线段树,也叫区间树,segmengt-tree,是一种长度不变的平衡树结构,父节点存储的结果是左右子节点的总计。以...

  • 数据结构-线段树

    实现一个线段树 下面实现的线段树,有三个功能: 把数组构建成一颗线段树 线段树的修改 线段树的查询 303号问题 ...

  • 各类线段树模板

    1.用数组维护线段树,可实现单点修改和区间查询。

  • 线段树

    [toc] 线段树 实现问题:常用于求数组区间最小值 时间复杂度:(1).建树复杂度:nlogn。(2).线段树算...

  • 算法笔记 - 线段树

    线段树的实现比较简单 时间复杂度 O(nlogn) 传统线段树一般用递归实现 线段树可以实现区间数值修改O(log...

  • 线段树

    线段树相关知识点梳理 1.线段树实现:包括add,update,query方法的实现 2.业务代码简单验证 ===...

  • 线段树模板

    线段树 线段树基本概念 概述 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组)...

  • 数据结构java描述

    接口 栈 队列 集合 并查集 映射 数组 链表 栈 数组实现 链表实现 队列 数组实现 链表实现 二分搜索树 集合...

  • Golang之数组和切片

    引用 数组、字符串和切片 Go数组中的索引问题 深入解析 Go 中 Slice 底层实现 Golang 入门 : ...

  • Golang数据结构 - 2 - 栈

    在上一章中,我们用Go实现了最常用的数据结构-数组,并实现了数组的添加元素、删除元素、数组遍历、数组排序和数组查找...

网友评论

    本文标题:(13)Go实现线段树-数组实现

    本文链接:https://www.haomeiwen.com/subject/rwkmgqtx.html