golang golang中一个笨拙的段树实现

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang golang中一个笨拙的段树实现相关的知识,希望对你有一定的参考价值。

package main

import (
	"errors"
	"fmt"
	"math"
)

func divide(l, r int) (int, error) {
	m := (l + r) / 2
	if l < m && m < r {
		return m, nil
	} else {
		return 0, errors.New(fmt.Sprintf("[%d, %d) cannot be divided", l, r))
	}
}

type elem_t int

type merge_func_t func(int, int, int, elem_t, elem_t) elem_t

type node_t struct {
	elem        elem_t
	left, right *node_t
}

type segment_tree_t struct {
	root   *node_t
	merge  merge_func_t
	length int
}

func build(a []elem_t, merge merge_func_t) segment_tree_t {
	var build_iter func(l, r int) *node_t
	build_iter = func(l, r int) *node_t {
		m, err := divide(l, r)
		if err != nil {
			return &node_t{a[l], nil, nil}
		}
		node := node_t{}
		node.left, node.right = build_iter(l, m), build_iter(m, r)
		node.elem = merge(l, m, r, node.left.elem, node.right.elem)

		return &node
	}

	t := segment_tree_t{}
	t.root = build_iter(0, len(a))
	t.merge = merge
	t.length = len(a)

	return t
}

func (t *segment_tree_t) query(l, r int) (elem_t, error) {
	if r-l < 1 {
		var e elem_t
		return e, errors.New("Invalid query")
	}
	if l < 0 {
		l = 0
	}
	if r > t.length {
		r = t.length
	}

	var query_iter func(node *node_t, l, r, ql, qr int) elem_t
	query_iter = func(node *node_t, l, r, ql, qr int) elem_t {
		m, err := divide(l, r)
		if err != nil || (l == ql && r == qr) {
			return node.elem
		}
		if qr <= m {
			return query_iter(node.left, l, m, ql, qr)
		}
		if m <= ql {
			return query_iter(node.right, m, r, ql, qr)
		}
		e0 := query_iter(node.left, l, m, ql, m)
		e1 := query_iter(node.right, m, r, m, qr)

		return t.merge(ql, m, qr, e0, e1)
	}
	return query_iter(t.root, 0, t.length, l, r), nil
}

func (t *segment_tree_t) update(p int, e elem_t) {
	if p < 0 || p >= t.length {
		return
	}

	var update_iter func(node *node_t, l, r int)
	update_iter = func(node *node_t, l, r int) {
		m, err := divide(l, r)
		if err != nil {
			node.elem = e
			return
		}
		if p < m {
			update_iter(node.left, l, m)
		} else {
			update_iter(node.right, m, r)
		}
		node.elem = t.merge(l, m, r, node.left.elem, node.right.elem)
	}
	update_iter(t.root, 0, t.length)
}

func print_iter(node *node_t, l, r int) {
	if m, err := divide(l, r); err != nil {
		fmt.Println("    leaf", l, r, node.elem)
	} else {
		fmt.Println("internal", l, r, node.elem)
		print_iter(node.left, l, m)
		print_iter(node.right, m, r)
	}
}

func main() {
	a := []elem_t{1, 2, 3, 2, 3, 2, 1}

	st := build(a, func(l, m, r int, e0, e1 elem_t) elem_t {
		return e0 + e1*elem_t(math.Pow10(m-l))
	})

	fmt.Println(st.query(1, 4))
	fmt.Println(st.query(3, 6))
	st.update(1, 1)
	st.update(2, 1)
	fmt.Println(st.query(1, 4))
	fmt.Println(st.query(3, 6))
	fmt.Println(st.query(5, 5))
	fmt.Println(st.query(5, 6))

	print_iter(st.root, 0, st.length)
}

golang中bufio包

参考技术A 一、介绍go标准库中的bufio
最近用golang写了一个处理文件的脚本,由于其中涉及到了文件读写,开始使用golang中的 io 包,后来发现golang 中提供了一个bufio的包,使用这个包可以大幅提高文件读写的效率,于是在网上搜索同样的文件读写为什么bufio 要比io 的读写更快速呢?根据网上的资料和阅读源码,以下来详细解释下bufio的高效如何实现的。

bufio 包介绍
bufio包实现了有缓冲的I/O。它包装一个io.Reader或io.Writer接口对象,创建另一个也实现了该接口,且同时还提供了缓冲和一些文本I/O的帮助函数的对象。

以上为官方包的介绍,在其中我们能了解到的信息如下:

bufio 是通过缓冲来提高效率

简单的说就是,把文件读取进缓冲(内存)之后再读取的时候就可以避免文件系统的io 从而提高速度。同理,在进行写操作时,先把文件写入缓冲(内存),然后由缓冲写入文件系统。看完以上解释有人可能会表示困惑了,直接把 内容->文件 和 内容->缓冲->文件相比, 缓冲区好像没有起到作用嘛。其实缓冲区的设计是为了存储多次的写入,最后一口气把缓冲区内容写入文件。下面会详细解释

bufio 封装了io.Reader或io.Writer接口对象,并创建另一个也实现了该接口的对象

io.Reader或io.Writer 接口实现read() 和 write() 方法,对于实现这个接口的对象都是可以使用这两个方法的

注明:介绍内容来自博主 LiangWenT
,原文链接: https://blog.csdn.net/LiangWenT/article/details/78995468 ,在查找资料时,发现这篇博客的内容很好理解

bufio包实现了缓存IO。它包装了io.Reader和io.Write对象,创建了另外的Reader和Writer对象,它们也实现了io.Reader和io.Write接口,具有缓存。注意:缓存是放在主存中,既然是保存在主存里,断电会丢失数据,那么要及时保存数据。

二、常用内容
1、Reader类型

NewReaderSize

作用:NewReaderSize将rd封装成一个带缓存的bufio.Reader对象。缓存大小由size指定(如果小于16则会被设为16)。如果rd的基类型就是有足够缓存的bufio.Reader类型,则直接将rd转换为基类型返回。
NewReader

funcReader相当于NewReaderSize(rd, 4096)
Peek

Peek返回缓存的一个切片,该切片引用缓存中前n个字节的数据,该操作不会将数据读出,只是引用,引用的数据在下一次读取操作之前有效的。如果切片长度小于n,则返回一个错误信息说明原因。如果n大于缓存的总大小,则返回ErrBufferFull。
Read

Read从b中数据到p中,返回读出的字节数和遇到的错误。如果缓存不为空,则只能读出缓冲中的数据,不会从底层io.Reader中提取数据,如果缓存为空,则:
1、len(p) >= 缓存大小,则跳过缓存,直接从底层io.Reader中读出到p中
2、len(p)< 缓存大小,则先将数据从底层io.Reader中读取到缓存中,再从缓存读取到p中。
Buffered

Buffered返回缓存中未读取的数据的长度。
Discard

Discard跳过后续的n个字节的数据,返回跳过的字节数。

Writer类型和方法
write结构

NewWriteSize

NewWriterSize将wr封装成一个带缓存的bufio.Writer对象,缓存大小由size指定(如果小于4096则会被设置未4096)。
NewWrite

NewWriter相等于NewWriterSize(wr, 4096)

WriteString

WriteString功能同Write,只不过写入的是字符串
WriteRune

WriteRune向b写入r的UTF-8编码,返回r的编码长度。
Flush

Available

Available 返回缓存中未使用的空间的长度
Buffered

Buffered返回缓存中未提交的数据长度
Reset

Reset将b的底层Write重新指定为w,同时丢弃缓存中的所有数据,复位所有标记和错误信息。相当于创建了一个新的bufio.Writer。

GO中还提供了Scanner类型,处理一些比较简单的场景。如处理按行读取输入序列或空格分隔的词等。
内容来自: https://blog.csdn.net/wangshubo1989/article/details/70177928

参考链接:
1) https://blog.csdn.net/LiangWenT/article/details/78995468
2) https://blog.csdn.net/wangshubo1989/article/details/70177928

以上是关于golang golang中一个笨拙的段树实现的主要内容,如果未能解决你的问题,请参考以下文章

STL 用于 C++ 中的段树

【golang】HashMap原理和实现

golang中bufio包

golang sync.Pool的用法及实现

从Golang中open的实现方式看Golang的语言设计

golang Golang中信号量的示例实现和使用