You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: Segment Tree/README.markdown
+116-57
Original file line number
Diff line number
Diff line change
@@ -2,12 +2,15 @@
2
2
3
3
I'm pleased to present to you Segment Tree. It's actually one of my favorite data structures because it's very flexible and simple in realization.
4
4
5
-
Let's suppose that you have array **a** of some type and some associative function _**f**_(e.g. sum, multiplication, min, max, gcd).
5
+
Let's suppose that you have an array **a** of some type and some associative function **f**. For example, the function can be sum, multiplication, min, max, [gcd](../Greatest Common Divisor/), and so on.
6
+
6
7
Your task is:
7
-
* answer a queries for given **l** and **r**: `f(a[l], a[l+1], ..., a[r-1], a[r])`
8
-
* support replacing item at some index `a[index] = newItem`
9
8
10
-
Here's naive approach if our array's type is Int and _**f**_ is just sum of two integers:
9
+
- answer a query for an interval given by **l** and **r**, i.e. perform `f(a[l], a[l+1], ..., a[r-1], a[r])`
10
+
- support replacing an item at some index `a[index] = newItem`
11
+
12
+
Here's naive approach if our array's type is `Int` and **f** is just the sum of two integers:
The running time of this algorithm is **O(n)** in worst case (**l = 0, r = n-1**). And if we have **m** queries to answer we get **O(m*n)** complexity.
21
-
If we have **n = 10^5** and **m = 100** our algorithm will do **10^7** units of work, ouh it's sounds not very good. Let's look how we can improve it.
22
23
23
-
Segment trees allow us answer a queries and replace items on **O(log n)**, isn't it magic?:sparkles:
24
-
The main idea of segment trees is simple: we precalculate some segments in our array and then we can use it without repeating calculation.
24
+
The running time of this algorithm is **O(n)** in the worst case, that is when **l = 0, r = n-1**. And if we have **m** queries to answer we get **O(m*n)** complexity.
25
+
26
+
If we have an array with 100,000 items (**n = 10^5**) and we have to do 100 queries (**m = 100**), then our algorithm will do **10^7** units of work. Ouch, that doesn't sound very good. Let's look at how we can improve it.
27
+
28
+
Segment trees allow us to answer queries and replace items with **O(log n)** time. Isn't it magic? :sparkles:
29
+
30
+
The main idea of segment trees is simple: we precalculate some segments in our array and then we can use those without repeating calculations.
31
+
25
32
## Structure of segment tree
26
33
27
-
Segment tree is just [binary tree](../Binary Tree/) where each node has:
28
-
*`leftBound`
29
-
*`rightBound`
30
-
*`value` is actually `f(a[leftBound], a[leftBound+1], .., a[rightBound])`
31
-
*`leftChild`
32
-
*`rightChild`
34
+
A segment tree is just a [binary tree](../Binary Tree/) where each node is an instance of the `SegmentTree` class:
35
+
36
+
```swift
37
+
publicclassSegmentTree<T> {
38
+
privatevar value: T
39
+
privatevar function: (T, T) -> T
40
+
privatevar leftBound: Int
41
+
privatevar rightBound: Int
42
+
privatevar leftChild: SegmentTree<T>?
43
+
privatevar rightChild: SegmentTree<T>?
44
+
}
45
+
```
46
+
47
+
Each node has the following data:
48
+
49
+
-`leftBound` and `rightBound` describe an interval
50
+
-`leftChild` and `rightChild` are pointers to child nodes
51
+
-`value` is actually the application of the function `f(a[leftBound], a[leftBound+1], ..., a[rightBound-1], a[rightBound])`
33
52
34
-
Here's structure of segment tree for given array `[1, 2, 3, 4]` and **f = a+b**, pairs of leftBound and rightBound marked in red
53
+
If our array is `[1, 2, 3, 4]` and the function `f = a + b`, the segment tree looks like this:
35
54
36
55

37
56
38
-
## Building segment tree
57
+
The `leftBound` and `rightBound` of each node are marked in red.
value =function(leftChild!.value, rightChild!.value)
77
+
78
+
value =function(leftChild!.value, rightChild!.value) // 4
53
79
}
54
80
}
55
81
```
56
82
57
-
If out current leftBound and rightBound are equal it means that we are in leaf so we don't have any child nodes and we just fill in `value` property with `array[leftBound]` else we have two child nodes. In that case we divide our current segment into two equal (if length is even) segments: `middle = (leftBound + rightBound) / 2`**[leftBound, middle]** and **[middle+1, rightBound]** and then we build our child nodes for that segments. After we build our child nodes so we can easily calculate our value as `value = function(leftChild!.value, rightChild!.value)` because **f(leftBound, rightBound) = f(f(leftBound, middle), f(middle+1, rightBound))**
83
+
Notice that this is a recursive method! You give it an array, such as `[1, 2, 3, 4]` and it creates the root node of the tree and all the child nodes as well.
84
+
85
+
1. The recursion terminates if `leftBound` and `rightBound` are equal. That means this `SegmentTree` instance will represent a leaf node. For the input array `[1, 2, 3, 4]`, it will create four such leaf nodes: `1`, `2`, `3`, and `4`. We just fill in the `value` property with the number from the array.
86
+
87
+
2. However, if `rightBound` is still greater than `leftBound`, we create two child nodes. We divide the current segment into two equal (if length is even) segments.
88
+
89
+
3. Recursively build child nodes for those two segments. The left child node covers the interval **[leftBound, middle]** and the right child node covers **[middle+1, rightBound]**.
90
+
91
+
4. After having constructed our child nodes, we can calculate our own value because **f(leftBound, rightBound) = f(f(leftBound, middle), f(middle+1, rightBound))**. It's math!
92
+
93
+
Building the tree is an **O(n)** operation.
58
94
59
95
## Getting answer to query
60
96
97
+
We go through all this trouble so we can efficiently query the tree.
98
+
99
+
Here's the code:
100
+
61
101
```swift
62
-
publicfuncqueryWithLeftBound(leftBound: Int, rightBound: Int) -> T {
sumSegmentTree.queryWithLeftBound(0, rightBound: 0) // just 1
156
+
sumSegmentTree.queryWithLeftBound(3, rightBound: 3) // just 4
103
157
```
104
158
105
-

159
+
Querying the tree takes **O(log n)** time.
106
160
107
161
## Replacing items
108
162
163
+
The value of a node in the segment tree depends on the nodes below it. So if we want to change a value of a leaf node, we need to update all its parent nodes too.
value =function(leftChild!.value, rightChild!.value)
177
+
value =function(leftChild.value, rightChild.value)
120
178
}
121
179
}
122
180
```
123
-
Foremost, we check if current node is leaf and if so we just change its value otherwise we need to find out to which child index belongs and call same function on that child. After that we recalculate our value because it needs to be in actual state.
124
181
125
-
## Examples
182
+
As usual, this works with recursion. If the node is a leaf, we just change its value. If the node is not a leaf, then we recursively call `replaceItemAtIndex()` to update its children. After that, we recalculate the node's own value so that it is up-to-date again.
183
+
184
+
Replacing an item takes **O(log n)** time.
126
185
127
-
Examples of using segment trees can be found in playground
186
+
See the playground for more examples of how to use the segment tree.
0 commit comments