树状数组 线段树 后缀数组

最近学习到新东西,有点意思
线段树

树状数组

范围内改单点求区间和

307 区域和检索
  • 树状数组
var n int
var tree []int

func lowBit(x int)int{
    return x&(-x)
}

//更新向上
func update(pos int, val int){
    pos++
    for pos<=n{
        tree[pos]+=val
        pos+=lowBit(pos)
    }
}

func query(pos int)int{
    res:=0
    for pos>0{
        res+=tree[pos]
        pos-=lowBit(pos)
    }
    return res
}

type NumArray struct {
   arr []int
}


func Constructor(nums []int) NumArray {
    var num NumArray
    n = len(nums) //从1开始
    tree = make([]int,n+1) 
    for i:=0;i<len(nums);i++{
        update(i,nums[i])
    }
    num.arr = make([]int,n)
    copy(num.arr,nums)
    return num
}


func (this *NumArray) Update(index int, val int)  {
    update(index,val-this.arr[index])
    this.arr[index] = val
}


func (this *NumArray) SumRange(left int, right int) int {
    return query(right+1)-query(left)    //整体加1
}

线段树

范围内数据求和或者最大值(O(logn)更新和查询)
有点和树状数组类似

  • 包含延迟标记
package main

import (
	"fmt"
)

//区间修改,区间查询,延迟标记

type Tree struct {
	l,r int  //左右索引
	sum int  //区间和
	add int  //延迟标记
}

var t []Tree  //线段树变量
var nums []int //具体数组的值,由此数组构成线段树

//构建线段树
func build(p,l,r int){  //l r 为数组左右边界下标,从1开始
	t[p].l,t[p].r= l,r
	if l==r{
		t[p].sum = nums[l]  //具体和需要数组
		return
	}
	mid:=(l+r)/2
	build(p*2,l,mid)
	build(p*2+1,mid+1,r) //右边子树
	t[p].sum = t[p*2].sum+t[p*2+1].sum //子树之和
}

func spread(p int){ //延迟修改标记   先更新左右节点信息,再打标记    带标记说明此层已经修改,下一层未修改
	if t[p].add>0{
		t[p*2].sum+=t[p].add*(t[p*2].r-t[p*2+1].l+1) //更左边子树区间和
		t[p*2+1].sum +=t[p].add*(t[p*2+1].r-t[p*2+1].l+1) //更右边
		t[p*2].add+=t[p].add  //左边标记
		t[p*2+1].add +=t[p].add
		t[p].add=0  //清除标记
	}
}

//区间修改,l到r 增加d,(如果修改单点,需要注意是增两者差值)
func update(p,l,r,d int){
	if l<=t[p].l&&r>=t[p].r{
		t[p].sum+=d*(t[p].r-t[p].l+1)
		t[p].add+=d
		return
	}
	// 执行延迟下标
	spread(p)
	mid:=(t[p].l+t[p].r)/2
	if l<=mid{
		update(p*2,l,r,d)
	}
	if r>mid{
		update(p*2+1,l,r,d)
	}
	//返回上层更新区间和,是重新计算
	t[p].sum = t[p*2].sum+t[p*2+1].sum
}

//区间查询
func ask(p,l,r int)int{
	if l<=t[p].l&&r>=t[p].r{
		return t[p].sum
	}
	spread(p) //执行延迟下标
	mid:=(t[p].l+t[p].r)/2
	val:=0
	if l<=mid{
		val+=ask(p*2,l,r)
	}
	if r>mid{
		val+=ask(p*2+1,l,r)
	}
	return val
}

//********************************************************************** 线段树 区间查询 已通过leetcode 307测试

//更改单点值
func updatePoint(index,val int){
	update(1,index+1,index+1,val-nums[index+1])
	nums[index+1] = val
}

func main(){
	nums = []int{0,1,2,3,4}  //0位置不使用,默认从1开始
	t=make([]Tree,4*len(nums))   //开四倍空间足够使用
 	build(1,1,4)
	fmt.Println(ask(1,1,4))  //1-4区间和

	updatePoint(0,2)  //下标1位置数据改为2

	fmt.Println(ask(1,1,4))  //1-4区间和
}

后缀数组

处理字符串所有后缀,最长公共前缀 (待更新)

posted @ 2022-05-21 17:47  海拉尔  阅读(32)  评论(0编辑  收藏  举报