mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-05 16:26:47 +08:00
algorithm: SegmentTree (#1178)
This commit is contained in:
97
Data-Structures/Tree/SegmentTree.js
Normal file
97
Data-Structures/Tree/SegmentTree.js
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/**
|
||||||
|
* Segment Tree
|
||||||
|
* concept : [Wikipedia](https://en.wikipedia.org/wiki/Segment_tree)
|
||||||
|
* inspired by : https://www.geeksforgeeks.org/segment-tree-efficient-implementation/
|
||||||
|
*
|
||||||
|
* time complexity
|
||||||
|
* - init : O(N)
|
||||||
|
* - update : O(log(N))
|
||||||
|
* - query : O(log(N))
|
||||||
|
*
|
||||||
|
* space complexity : O(N)
|
||||||
|
*/
|
||||||
|
class SegmentTree {
|
||||||
|
size
|
||||||
|
tree
|
||||||
|
constructor (arr) {
|
||||||
|
// we define tree like this
|
||||||
|
// tree[1] : root node of tree
|
||||||
|
// tree[i] : i'th node
|
||||||
|
// tree[i * 2] : i'th left child
|
||||||
|
// tree[i * 2 + 1] : i'th right child
|
||||||
|
// and we use bit, shift operation for index
|
||||||
|
this.size = arr.length
|
||||||
|
this.tree = new Array(2 * arr.length)
|
||||||
|
this.tree.fill(0)
|
||||||
|
|
||||||
|
this.build(arr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// function to build the tree
|
||||||
|
build (arr) {
|
||||||
|
const { size, tree } = this
|
||||||
|
// insert leaf nodes in tree
|
||||||
|
// leaf nodes will start from index N
|
||||||
|
// in this array and will go up to index (2 * N – 1)
|
||||||
|
for (let i = 0; i < size; i++) {
|
||||||
|
tree[size + i] = arr[i]
|
||||||
|
}
|
||||||
|
|
||||||
|
// build the tree by calculating parents
|
||||||
|
// tree's root node will contain all leaf node's sum
|
||||||
|
for (let i = size - 1; i > 0; --i) {
|
||||||
|
// current node's value is the sum of left child, right child
|
||||||
|
tree[i] = tree[i * 2] + tree[i * 2 + 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
update (index, value) {
|
||||||
|
const { size, tree } = this
|
||||||
|
|
||||||
|
// only update values in the parents of the given node being changed.
|
||||||
|
// to get the parent move to parent node (index / 2)
|
||||||
|
|
||||||
|
// set value at position index
|
||||||
|
index += size
|
||||||
|
// tree[index] is leaf node and index's value of array
|
||||||
|
tree[index] = value
|
||||||
|
|
||||||
|
// move upward and update parents
|
||||||
|
for (let i = index; i > 1; i >>= 1) {
|
||||||
|
// i ^ 1 turns (2 * i) to (2 * i + 1)
|
||||||
|
// i ^ 1 is second child
|
||||||
|
tree[i >> 1] = tree[i] + tree[i ^ 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// interval [L,R) with left index(L) included and right (R) excluded.
|
||||||
|
query (left, right) {
|
||||||
|
const { size, tree } = this
|
||||||
|
// cause R is excluded, increase right for convenient
|
||||||
|
right++
|
||||||
|
let res = 0
|
||||||
|
|
||||||
|
// loop to find the sum in the range
|
||||||
|
for (left += size, right += size; left < right; left >>= 1, right >>= 1) {
|
||||||
|
// L is the left border of an query interval
|
||||||
|
|
||||||
|
// if L is odd it means that it is the right child of its parent and our interval includes only L and not the parent.
|
||||||
|
// So we will simply include this node to sum and move to the parent of its next node by doing L = (L + 1) / 2.
|
||||||
|
|
||||||
|
// if L is even it is the left child of its parent
|
||||||
|
// and the interval includes its parent also unless the right borders interfere.
|
||||||
|
if ((left & 1) > 0) {
|
||||||
|
res += tree[left++]
|
||||||
|
}
|
||||||
|
|
||||||
|
// same in R (the right border of an query interval)
|
||||||
|
if ((right & 1) > 0) {
|
||||||
|
res += tree[--right]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { SegmentTree }
|
16
Data-Structures/Tree/test/SegmentTree.test.js
Normal file
16
Data-Structures/Tree/test/SegmentTree.test.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { SegmentTree } from '../SegmentTree'
|
||||||
|
|
||||||
|
describe('SegmentTree sum test', () => {
|
||||||
|
const a = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12]
|
||||||
|
|
||||||
|
const segment = new SegmentTree(a)
|
||||||
|
|
||||||
|
it('init sum check', () => {
|
||||||
|
expect(segment.query(0, 2)).toBe(6)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('init sum check', () => {
|
||||||
|
segment.update(2, 1)
|
||||||
|
expect(segment.query(0, 2)).toBe(4)
|
||||||
|
})
|
||||||
|
})
|
Reference in New Issue
Block a user