Skip to content

Commit 52df2c5

Browse files
committed
Improvements to segment tree description and code
1 parent 3b8331d commit 52df2c5

File tree

4 files changed

+164
-93
lines changed

4 files changed

+164
-93
lines changed

README.markdown

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ Basic sorts:
5656

5757
- [Insertion Sort](Insertion Sort/)
5858
- [Selection Sort](Selection Sort/)
59-
- Shell Sort
59+
- [Shell Sort](Shell Sort/)
6060

6161
Fast sorts:
6262

@@ -93,6 +93,7 @@ Bad sorting algorithms (don't use these!):
9393
### Machine learning
9494

9595
- k-Nearest Neighbors
96+
- PageRank
9697

9798
## Data structures
9899

Segment Tree/README.markdown

+116-57
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22

33
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.
44

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+
67
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`
98

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:
13+
1114
```swift
1215
func query(array: [Int], l: Int, r: Int) -> Int {
1316
var sum = 0
@@ -17,117 +20,173 @@ func query(array: [Int], l: Int, r: Int) -> Int {
1720
return sum
1821
}
1922
```
20-
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.
2223

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+
2532
## Structure of segment tree
2633

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+
public class SegmentTree<T> {
38+
private var value: T
39+
private var function: (T, T) -> T
40+
private var leftBound: Int
41+
private var rightBound: Int
42+
private var leftChild: SegmentTree<T>?
43+
private var 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])`
3352

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:
3554

3655
![structure](Images/Structure.png)
3756

38-
## Building segment tree
57+
The `leftBound` and `rightBound` of each node are marked in red.
58+
59+
## Building a segment tree
60+
61+
Here's how we create a node of the segment tree:
3962

40-
Let's see how to build node of segment tree.
4163
```swift
42-
init(array: [T], leftBound: Int, rightBound: Int, function: (T, T) -> T) {
64+
public init(array: [T], leftBound: Int, rightBound: Int, function: (T, T) -> T) {
4365
self.leftBound = leftBound
4466
self.rightBound = rightBound
4567
self.function = function
46-
if leftBound == rightBound {
68+
69+
if leftBound == rightBound { // 1
4770
value = array[leftBound]
4871
} else {
49-
let middle = (leftBound + rightBound) / 2
72+
let middle = (leftBound + rightBound) / 2 // 2
73+
74+
// 3
5075
leftChild = SegmentTree<T>(array: array, leftBound: leftBound, rightBound: middle, function: function)
5176
rightChild = SegmentTree<T>(array: array, leftBound: middle+1, rightBound: rightBound, function: function)
52-
value = function(leftChild!.value, rightChild!.value)
77+
78+
value = function(leftChild!.value, rightChild!.value) // 4
5379
}
5480
}
5581
```
5682

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.
5894

5995
## Getting answer to query
6096

97+
We go through all this trouble so we can efficiently query the tree.
98+
99+
Here's the code:
100+
61101
```swift
62-
public func queryWithLeftBound(leftBound: Int, rightBound: Int) -> T {
63-
if self.leftBound == leftBound && self.rightBound == rightBound { {
102+
public func queryWithLeftBound(leftBound: Int, rightBound: Int) -> T {
103+
// 1
104+
if self.leftBound == leftBound && self.rightBound == rightBound {
64105
return self.value
65-
} else if leftChild!.rightBound < leftBound {
66-
return rightChild!.queryWithLeftBound(leftBound, rightBound: rightBound)
67-
} else if rightChild!.leftBound > rightBound {
68-
return leftChild!.queryWithLeftBound(leftBound, rightBound: rightBound)
106+
}
107+
108+
guard let leftChild = leftChild else { fatalError("leftChild should not be nil") }
109+
guard let rightChild = rightChild else { fatalError("rightChild should not be nil") }
110+
111+
// 2
112+
if leftChild.rightBound < leftBound {
113+
return rightChild.queryWithLeftBound(leftBound, rightBound: rightBound)
114+
115+
// 3
116+
} else if rightChild.leftBound > rightBound {
117+
return leftChild.queryWithLeftBound(leftBound, rightBound: rightBound)
118+
119+
// 4
69120
} else {
70-
let leftResult = leftChild!.queryWithLeftBound(leftBound, rightBound: leftChild!.rightBound)
71-
let rightResult = rightChild!.queryWithLeftBound(rightChild!.leftBound, rightBound: rightBound)
121+
let leftResult = leftChild.queryWithLeftBound(leftBound, rightBound: leftChild.rightBound)
122+
let rightResult = rightChild.queryWithLeftBound(rightChild.leftBound, rightBound: rightBound)
72123
return function(leftResult, rightResult)
73124
}
74125
}
75126
```
76-
Firstly, we check if current query segment is equal to segment for which our current node responsible, if it is we just return its value.
77-
```swift
78-
return self.value
79-
```
127+
128+
Again, this is a recursive method. It checks four different possibilities.
129+
130+
1) First, we check if the query segment is equal to the segment for which our current node is responsible. If it is we just return this node's value.
80131

81132
![equalSegments](Images/EqualSegments.png)
82133

83-
Else we check that our query segment fully lies in rightChild, if so we return result of query on rightChild
84-
```swift
85-
return rightChild!.queryWithLeftBound(leftBound, rightBound: rightBound)
86-
```
134+
2) Does the query segment fully lie within the right child? If so, recursively perform the query on the right child.
87135

88136
![rightSegment](Images/RightSegment.png)
89137

90-
else if segment lies in leftChild we return result of query on leftChild.
91-
```swift
92-
return leftChild!.queryWithLeftBound(leftBound, rightBound: rightBound)
93-
```
138+
3) Does the query segment fully lie within the left child? If so, recursively perform the query on the left child.
94139

95140
![leftSegment](Images/LeftSegment.png)
96141

97-
If none of above-descripted runs it means our query lies in both child so we combine results of query on both child.
142+
4) If none of the above, it means our query partially lies in both children so we combine the results of queries on both children.
143+
144+
![mixedSegment](Images/MixedSegment.png)
145+
146+
For example, this is how you could test it out in a playground:
98147

99148
```swift
100-
let leftResult = leftChild!.queryWithLeftBound(leftBound, rightBound: leftChild!.rightBound)
101-
let rightResult = rightChild!.queryWithLeftBound(rightChild!.leftBound, rightBound: rightBound)
102-
return function(leftResult, rightResult)
149+
let array = [1, 2, 3, 4]
150+
151+
let sumSegmentTree = SegmentTree(array: array, function: +)
152+
153+
sumSegmentTree.queryWithLeftBound(0, rightBound: 3) // 1 + 2 + 3 + 4 = 10
154+
sumSegmentTree.queryWithLeftBound(1, rightBound: 2) // 2 + 3 = 5
155+
sumSegmentTree.queryWithLeftBound(0, rightBound: 0) // just 1
156+
sumSegmentTree.queryWithLeftBound(3, rightBound: 3) // just 4
103157
```
104158

105-
![mixedSegment](Images/MixedSegment.png)
159+
Querying the tree takes **O(log n)** time.
106160

107161
## Replacing items
108162

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.
164+
165+
Here is the code:
166+
109167
```swift
110-
public func replaceItemAtIndex(index: Int, withItem item: T) {
168+
public func replaceItemAtIndex(index: Int, withItem item: T) {
111169
if leftBound == rightBound {
112170
value = item
113-
} else {
114-
if leftChild!.rightBound >= index {
115-
leftChild!.replaceItemAtIndex(index, withItem: item)
171+
} else if let leftChild = leftChild, rightChild = rightChild {
172+
if leftChild.rightBound >= index {
173+
leftChild.replaceItemAtIndex(index, withItem: item)
116174
} else {
117-
rightChild!.replaceItemAtIndex(index, withItem: item)
175+
rightChild.replaceItemAtIndex(index, withItem: item)
118176
}
119-
value = function(leftChild!.value, rightChild!.value)
177+
value = function(leftChild.value, rightChild.value)
120178
}
121179
}
122180
```
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.
124181

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.
126185

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.
128187

129188
## See also
130189

131-
[Segment tree](http://wcipeg.com/wiki/Segment_tree)
190+
[Segment tree at PEGWiki](http://wcipeg.com/wiki/Segment_tree)
132191

133192
*Written for Swift Algorithm Club by [Artur Antonov](https://github.com/goingreen)*

Segment Tree/SegmentTree.playground/Contents.swift

+20-15
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,11 @@ public class SegmentTree<T> {
99
private var leftChild: SegmentTree<T>?
1010
private var rightChild: SegmentTree<T>?
1111

12-
13-
init(array: [T], leftBound: Int, rightBound: Int, function: (T, T) -> T) {
12+
public init(array: [T], leftBound: Int, rightBound: Int, function: (T, T) -> T) {
1413
self.leftBound = leftBound
1514
self.rightBound = rightBound
1615
self.function = function
16+
1717
if leftBound == rightBound {
1818
value = array[leftBound]
1919
} else {
@@ -24,34 +24,39 @@ public class SegmentTree<T> {
2424
}
2525
}
2626

27-
convenience init(array: [T], function: (T, T) -> T) {
27+
public convenience init(array: [T], function: (T, T) -> T) {
2828
self.init(array: array, leftBound: 0, rightBound: array.count-1, function: function)
2929
}
3030

3131
public func queryWithLeftBound(leftBound: Int, rightBound: Int) -> T {
3232
if self.leftBound == leftBound && self.rightBound == rightBound {
3333
return self.value
34-
} else if leftChild!.rightBound < leftBound {
35-
return rightChild!.queryWithLeftBound(leftBound, rightBound: rightBound)
36-
} else if rightChild!.leftBound > rightBound {
37-
return leftChild!.queryWithLeftBound(leftBound, rightBound: rightBound)
34+
}
35+
36+
guard let leftChild = leftChild else { fatalError("leftChild should not be nil") }
37+
guard let rightChild = rightChild else { fatalError("rightChild should not be nil") }
38+
39+
if leftChild.rightBound < leftBound {
40+
return rightChild.queryWithLeftBound(leftBound, rightBound: rightBound)
41+
} else if rightChild.leftBound > rightBound {
42+
return leftChild.queryWithLeftBound(leftBound, rightBound: rightBound)
3843
} else {
39-
let leftResult = leftChild!.queryWithLeftBound(leftBound, rightBound: leftChild!.rightBound)
40-
let rightResult = rightChild!.queryWithLeftBound(rightChild!.leftBound, rightBound: rightBound)
44+
let leftResult = leftChild.queryWithLeftBound(leftBound, rightBound: leftChild.rightBound)
45+
let rightResult = rightChild.queryWithLeftBound(rightChild.leftBound, rightBound: rightBound)
4146
return function(leftResult, rightResult)
4247
}
4348
}
44-
49+
4550
public func replaceItemAtIndex(index: Int, withItem item: T) {
4651
if leftBound == rightBound {
4752
value = item
48-
} else {
49-
if leftChild!.rightBound >= index {
50-
leftChild!.replaceItemAtIndex(index, withItem: item)
53+
} else if let leftChild = leftChild, rightChild = rightChild {
54+
if leftChild.rightBound >= index {
55+
leftChild.replaceItemAtIndex(index, withItem: item)
5156
} else {
52-
rightChild!.replaceItemAtIndex(index, withItem: item)
57+
rightChild.replaceItemAtIndex(index, withItem: item)
5358
}
54-
value = function(leftChild!.value, rightChild!.value)
59+
value = function(leftChild.value, rightChild.value)
5560
}
5661
}
5762
}

0 commit comments

Comments
 (0)