├── LICENSE ├── README.md ├── bench_test.go ├── example_test.go ├── list.go ├── list_test.go └── res └── checklist.jpg /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 2-Clause License 2 | 3 | Copyright (c) 2017, Stefan Nilsson 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 19 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 20 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 22 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 23 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Your basic Fenwick tree [![GoDoc](https://godoc.org/github.com/yourbasic/fenwick?status.svg)][godoc-fenwick] 2 | 3 | ### Go list data structure supporting prefix sums 4 | 5 | A Fenwick tree, or binary indexed tree, is a space-efficient 6 | list data structure that can efficiently update elements and 7 | calculate prefix sums in a list of numbers. 8 | 9 | ![Checklist](res/checklist.jpg) 10 | 11 | ### Installation 12 | 13 | Once you have [installed Go][golang-install], run this command 14 | to install the `fenwick` package: 15 | 16 | go get github.com/yourbasic/fenwick 17 | 18 | ### Documentation 19 | 20 | There is an online reference for the package at 21 | [godoc.org/github.com/yourbasic/fenwick][godoc-fenwick]. 22 | 23 | ### Roadmap 24 | 25 | * The API of this library is frozen. 26 | * Version numbers adhere to [semantic versioning][sv]. 27 | 28 | The only accepted reason to modify the API of this package 29 | is to handle issues that can't be resolved in any other 30 | reasonable way. 31 | 32 | Stefan Nilsson – [korthaj](https://github.com/korthaj) 33 | 34 | [godoc-fenwick]: https://godoc.org/github.com/yourbasic/fenwick 35 | [golang-install]: http://golang.org/doc/install.html 36 | [sv]: http://semver.org/ 37 | -------------------------------------------------------------------------------- /bench_test.go: -------------------------------------------------------------------------------- 1 | package fenwick 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | const n = 1000 8 | 9 | func BenchmarkNew(b *testing.B) { 10 | a := make([]int64, n) 11 | b.ResetTimer() 12 | for i := 0; i < b.N; i++ { 13 | _ = New(a...) 14 | } 15 | } 16 | 17 | func BenchmarkAppend(b *testing.B) { 18 | l := new(List) 19 | b.ResetTimer() 20 | for i := 0; i < b.N; i++ { 21 | for j := 0; j < n; j++ { 22 | l.Append(int64(j)) 23 | } 24 | } 25 | } 26 | 27 | func BenchmarkSum(b *testing.B) { 28 | a := make([]int64, n) 29 | l := New(a...) 30 | b.ResetTimer() 31 | for i := 0; i < b.N; i++ { 32 | for j := 0; j < n; j++ { 33 | _ = l.Sum(j) 34 | } 35 | } 36 | } 37 | 38 | func BenchmarkGet(b *testing.B) { 39 | a := make([]int64, n) 40 | l := New(a...) 41 | b.ResetTimer() 42 | for i := 0; i < b.N; i++ { 43 | for j := 0; j < n; j++ { 44 | _ = l.Get(j) 45 | } 46 | } 47 | } 48 | 49 | func BenchmarkSet(b *testing.B) { 50 | a := make([]int64, n) 51 | l := New(a...) 52 | b.ResetTimer() 53 | for i := 0; i < b.N; i++ { 54 | for j := 0; j < n; j++ { 55 | l.Set(j, int64(j)) 56 | } 57 | } 58 | } 59 | 60 | func BenchmarkAdd(b *testing.B) { 61 | a := make([]int64, n) 62 | l := New(a...) 63 | b.ResetTimer() 64 | for i := 0; i < b.N; i++ { 65 | for j := 0; j < n; j++ { 66 | l.Add(j, int64(j)) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /example_test.go: -------------------------------------------------------------------------------- 1 | package fenwick_test 2 | 3 | import ( 4 | "fmt" 5 | "github.com/yourbasic/fenwick" 6 | ) 7 | 8 | // Compute the sum of the first 4 elements in a list. 9 | func Example() { 10 | a := fenwick.New(1, 2, 3, 4, 5) 11 | fmt.Println(a.Sum(4)) 12 | // Output: 10 13 | } 14 | -------------------------------------------------------------------------------- /list.go: -------------------------------------------------------------------------------- 1 | // Package fenwick provides a list data structure supporting prefix sums. 2 | // 3 | // A Fenwick tree, or binary indexed tree, is a space-efficient list 4 | // data structure that can efficiently update elements and calculate 5 | // prefix sums in a list of numbers. 6 | // 7 | // Compared to a common array, a Fenwick tree achieves better balance 8 | // between element update and prefix sum calculation – both operations 9 | // run in O(log n) time – while using the same amount of memory. 10 | // This is achieved by representing the list as an implicit tree, 11 | // where the value of each node is the sum of the numbers in that 12 | // subtree. 13 | // 14 | package fenwick 15 | 16 | // List represents a list of numbers with support for efficient 17 | // prefix sum computation. The zero value is an empty list. 18 | type List struct { 19 | // The tree slice stores range sums of an underlying array t. 20 | // To compute the prefix sum t[0] + t[1] + t[k-1], add elements 21 | // which correspond to each 1 bit in the binary expansion of k. 22 | // 23 | // For example, this is how the sum of the 13 first elements 24 | // in t is computed: 13 is 1101₂ in binary, so the elements 25 | // at indices 1101₂ - 1, 1100₂ - 1, and 1000₂ - 1 are added; 26 | // they contain the range sums t[12], t[8] + … t[11], and 27 | // t[0] + … + t[7], respectively. 28 | // 29 | tree []int64 30 | } 31 | 32 | // New creates a new list with the given elements. 33 | func New(n ...int64) *List { 34 | len := len(n) 35 | t := make([]int64, len) 36 | copy(t, n) 37 | for i := range t { 38 | if j := i | (i + 1); j < len { 39 | t[j] += t[i] 40 | } 41 | } 42 | return &List{ 43 | tree: t, 44 | } 45 | } 46 | 47 | // Len returns the number of elements in the list. 48 | func (l *List) Len() int { 49 | return len(l.tree) 50 | } 51 | 52 | // Get returns the element at index i. 53 | func (l *List) Get(i int) int64 { 54 | sum := l.tree[i] 55 | j := i + 1 56 | j -= j & -j 57 | for i > j { 58 | sum -= l.tree[i-1] 59 | i -= i & -i 60 | } 61 | return sum 62 | } 63 | 64 | // Set sets the element at index i to n. 65 | func (l *List) Set(i int, n int64) { 66 | n -= l.Get(i) 67 | for len := len(l.tree); i < len; i |= i + 1 { 68 | l.tree[i] += n 69 | } 70 | } 71 | 72 | // Add adds n to the element at index i. 73 | func (l *List) Add(i int, n int64) { 74 | for len := len(l.tree); i < len; i |= i + 1 { 75 | l.tree[i] += n 76 | } 77 | } 78 | 79 | // Sum returns the sum of the elements from index 0 to index i-1. 80 | func (l *List) Sum(i int) int64 { 81 | var sum int64 82 | for i > 0 { 83 | sum += l.tree[i-1] 84 | i -= i & -i 85 | } 86 | return sum 87 | } 88 | 89 | // SumRange returns the sum of the elements from index i to index j-1. 90 | func (l *List) SumRange(i, j int) int64 { 91 | var sum int64 92 | for j > i { 93 | sum += l.tree[j-1] 94 | j -= j & -j 95 | } 96 | for i > j { 97 | sum -= l.tree[i-1] 98 | i -= i & -i 99 | } 100 | return sum 101 | } 102 | 103 | // Append appends a new element to the end of the list. 104 | func (l *List) Append(n int64) { 105 | i := len(l.tree) 106 | l.tree = append(l.tree, 0) 107 | l.tree[i] = n - l.Get(i) 108 | } 109 | -------------------------------------------------------------------------------- /list_test.go: -------------------------------------------------------------------------------- 1 | package fenwick 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGet(t *testing.T) { 8 | n := 100 9 | a := make([]int64, n) 10 | for i := range a { 11 | a[i] = int64(i) 12 | } 13 | l := New(a...) 14 | for i := range a { 15 | if a[i] != l.Get(i) { 16 | t.Errorf("Get(%d) = %d; want %d", i, l.Get(i), a[i]) 17 | } 18 | } 19 | } 20 | 21 | func TestSet(t *testing.T) { 22 | n := 100 23 | a := make([]int64, n) 24 | for i := range a { 25 | a[i] = int64(i) 26 | } 27 | l := New(a...) 28 | for i := range a { 29 | l.Set(i, 100) 30 | if l.Get(i) != 100 { 31 | t.Errorf("Get(%d) = %d; want %d", i, l.Get(i), 100) 32 | } 33 | } 34 | } 35 | 36 | func TestAdd(t *testing.T) { 37 | n := 100 38 | a := make([]int64, n) 39 | for i := range a { 40 | a[i] = int64(i) 41 | } 42 | l := New(a...) 43 | for i := range a { 44 | l.Add(i, 100) 45 | if l.Get(i) != a[i]+100 { 46 | t.Errorf("Get(%d) = %d; want %d", i, l.Get(i), a[i]+100) 47 | } 48 | } 49 | } 50 | 51 | func TestSum(t *testing.T) { 52 | n := 100 53 | a := make([]int64, n) 54 | for i := range a { 55 | a[i] = int64(i) 56 | } 57 | l := New(a...) 58 | for i := range a { 59 | var res int64 60 | for j := 0; j < i; j++ { 61 | res += a[j] 62 | } 63 | if l.Sum(i) != res { 64 | t.Errorf("Sum(%d) = %d; want %d", i, l.Get(i), res) 65 | } 66 | } 67 | } 68 | 69 | func TestSumRange(t *testing.T) { 70 | n := 100 71 | a := make([]int64, n) 72 | for i := range a { 73 | a[i] = int64(i) 74 | } 75 | l := New(a...) 76 | for i := range a { 77 | for j := i; j < n; j++ { 78 | var res int64 79 | for k := i; k < j; k++ { 80 | res += a[k] 81 | } 82 | if l.SumRange(i, j) != res { 83 | t.Errorf("SumRange(%d, %d) = %d; want %d", i, j, l.SumRange(i, j), res) 84 | } 85 | } 86 | } 87 | } 88 | 89 | func TestAppend(t *testing.T) { 90 | n := 100 91 | a := make([]int64, n) 92 | l := new(List) 93 | if l.Len() != 0 { 94 | t.Errorf("Len() = %d; want %d", l.Len(), 0) 95 | } 96 | for i := range a { 97 | a[i] = int64(i) 98 | l.Append(int64(i)) 99 | } 100 | if l.Len() != n { 101 | t.Errorf("Len() = %d; want %d", l.Len(), n) 102 | } 103 | for i := range a { 104 | var res int64 105 | for j := 0; j < i; j++ { 106 | res += a[j] 107 | } 108 | if l.Get(i) != a[i] { 109 | t.Errorf("Get(%d) = %d; want %d", i, l.Get(i), a[i]) 110 | } 111 | if l.Sum(i) != res { 112 | t.Errorf("Sum(%d) = %d; want %d", i, l.Sum(i), res) 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /res/checklist.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yourbasic/fenwick/5f8823d88d1535c8a70acfaeeb7caab77427dbbd/res/checklist.jpg --------------------------------------------------------------------------------