Skip to content

Commit 5700e07

Browse files
committed
Add Bottom-up Segment Tree
1 parent 3a7f20d commit 5700e07

File tree

11 files changed

+897
-0
lines changed

11 files changed

+897
-0
lines changed
Lines changed: 352 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,352 @@
1+
/*:
2+
[Previous](@previous) | [Next](@next)
3+
***
4+
5+
# Bottom-up Segment Tree
6+
7+
## Structure of segment tree
8+
9+
A Bottom-up Segment Tree is decribed by an array which store the values of original array and some calculated results. The major difference between Bottom-up Segment Tree and Segment Tree are type of stored properties and instance build sequences. Here are some preparations in advanced of implementing a Bottom-up Segment Tree class, and we can use it generically in the future:
10+
*/
11+
import Foundation
12+
13+
public protocol InitializeWithoutParametersable: Comparable {
14+
init()
15+
static var nomalizeValue: Self {get}
16+
}
17+
extension Int: InitializeWithoutParametersable {
18+
public static var nomalizeValue: Int {
19+
return 1
20+
}
21+
}
22+
extension Double: InitializeWithoutParametersable {
23+
public static var nomalizeValue: Double {
24+
return 1.0
25+
}
26+
}
27+
extension String: InitializeWithoutParametersable {
28+
public static var nomalizeValue: String {
29+
return ""
30+
}
31+
}
32+
/*:
33+
Each node of a Bottom-up Segment Tree lives inside an array, so it doesn't use parent/child pointers.
34+
*/
35+
36+
public class BottomUpSegmentTree<T: InitializeWithoutParametersable> {
37+
private var arrayCount: Int!
38+
private var leavesCapacity: Int!
39+
private var function: (T, T) -> T
40+
private var associatedtypeInitValue: T!
41+
private var result: [T]!
42+
43+
/*:
44+
- Each element of `result` array is a node of tree, and its value is the result of applying the function `f(a[leftBound], a[leftBound+1], ..., a[rightBound-1], a[rightBound])`.
45+
- `leftBound` and `rightBound` describe an interval
46+
- `arrayCount` and `leavesCapacity` are the count of array and maximun count of the source array can appending elements without increaseing the result array capacity.
47+
- `value` is the result of applying the function `f(a[leftBound], a[leftBound+1], ..., a[rightBound-1], a[rightBound])`
48+
49+
![](BUST1.png)
50+
51+
### Bitwise Operators
52+
53+
If `i` is the index of a node, then the following formulas give the array indices of its parent and child nodes:
54+
55+
parent(i) = i>>1
56+
left(i) = i<<1
57+
right(i) = i<<1 + 1
58+
59+
60+
If our array is `[1, 2, 3, 4]` and the function `f = a + b`, the segment tree looks like this:
61+
62+
`[0, 10, 3, 7, 1, 2, 3, 4]`
63+
64+
For example, the `result[2] = 3` is the result of applying the function `f(a[0], a[1]`. `a[0]` and `a[1]` are stored in `result[4]` and 'result[5]`, and their parent is stored in `result[2]`.
65+
> Note: Please note that the start index of the node of a BottomUpSegmentTree is `1`. The value stored in the `result[0]` is useless.
66+
67+
## Building a bottom-up segment tree
68+
69+
Here's how we create a instance of the bottom-up segment tree:
70+
71+
*/
72+
// For best applies to most of functions, initialize an instance with providing associated type initial Value.
73+
public init(array: [T], function: (T, T) -> T, associatedtypeInitValue: T) {
74+
self.function = function
75+
self.associatedtypeInitValue = associatedtypeInitValue
76+
self.arrayCount = array.count
77+
self.leavesCapacity = leavesCapacityUpdate(array)
78+
self.result = resultUpdate(array)
79+
}
80+
// Normally, for most calculating functions, T() = (0) or T.nomalizeValue = (1) are the best initial value of a numeric associated type.
81+
public init(array: [T], function: (T, T) -> T, associatedtypeNeedInitValue: Bool) {
82+
self.function = function
83+
if associatedtypeNeedInitValue {
84+
self.associatedtypeInitValue = T.nomalizeValue
85+
} else {
86+
self.associatedtypeInitValue = T()
87+
}
88+
self.arrayCount = array.count
89+
// 1
90+
self.leavesCapacity = leavesCapacityUpdate(array)
91+
// 2 & 3
92+
self.result = resultUpdate(array)
93+
}
94+
// simply use T() = (0) for initialize an associated type .
95+
public convenience init(array: [T], function: (T, T) -> T) {
96+
self.init(array: array, function: function, associatedtypeNeedInitValue: false)
97+
}
98+
99+
// helping methods for build an BottomUpSegmentTree instance
100+
private func leavesCapacityUpdate(array: [T]) -> Int {
101+
var n = array.count - 1
102+
for i in 1...64 {
103+
n /= 2
104+
if n == 0 {
105+
// 1
106+
return 1<<i
107+
}
108+
}
109+
return 0
110+
}
111+
112+
private func resultUpdate(array: [T]) -> [T] {
113+
// 2
114+
var result = [T](count: leavesCapacity * 2, repeatedValue: associatedtypeInitValue)
115+
for i in array.indices {
116+
// 3
117+
result[leavesCapacity+i] = array[i]
118+
}
119+
// 4
120+
for j in (leavesCapacity-1).stride(to: 0, by: -1) {
121+
result[j] = function(result[j << 1], result[(j << 1) + 1])
122+
}
123+
return result
124+
}
125+
}
126+
/*:
127+
Notice that this is non-recursive method. You give it an array such as `[1, 2, 3, 4]` and it builds up the entire tree, from the leaf node to all the parent nodes using loop operations. `result[j] = function(result[j<<1], result[(j<<1)+1])` describe the value of a parent node index `j` is the result of applying function to children nodes index `j>>1` & `(j<<1) + 1`
128+
129+
1. Determine the `leavesCapacity` maximum numbers of leaves nodes we need to prepare for tree build. The value of `leavesCapacity` is `2^x` where `x` is the minimum interger to make `2^x` just greater than or equal the count of input array. For example, `[1,2,3,4,5,6]` is the input array and it's count is `6`, so the `leavesCapacity = 2^3 = 8`.
130+
131+
2. Creat an array with doule of leavesCapacity.
132+
133+
3. Put elements of input array to tree. The relation of indexies of both array is `result[leavesCapacity+i] = array[i]`
134+
135+
4. Update the remaining values from leaves till the root.
136+
137+
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!
138+
139+
Building the tree is an **O(n)** operation.
140+
141+
## Getting answer to query
142+
143+
We go through all this trouble so we can efficiently query the tree.
144+
145+
Here's the code of query methodes:
146+
147+
*/
148+
extension BottomUpSegmentTree {
149+
// MARK: - query
150+
151+
public func queryWithLeftBound(leftBound: Int, rightBound: Int) -> T {
152+
// 1
153+
guard leftBound >= 0 && leftBound <= arrayCount && rightBound >= 0 && rightBound <= arrayCount else { fatalError("index error") }
154+
guard leftBound <= rightBound else { fatalError("leftBound should be equal or less than rightBound") }
155+
// 2
156+
var s = leftBound + leavesCapacity - 1, t = rightBound + leavesCapacity + 1
157+
var ans = associatedtypeInitValue
158+
while s ^ t != 1 {
159+
// result will be effected if the sequence of input arguments is different
160+
ans = s & 1 == 0 ? function(ans, result[s+1]) : ans
161+
ans = t & 1 == 1 ? function(result[t-1], ans) : ans
162+
s>>=1
163+
t>>=1
164+
}
165+
return ans
166+
}
167+
168+
public func queryWithRange(range: Range<Int>) -> T {
169+
return queryWithLeftBound(range.startIndex, rightBound: range.endIndex - 1)
170+
}
171+
}
172+
/*:
173+
Again, this is a recursive method. It checks four different possibilities.
174+
175+
![](BUST2.png)
176+
![](BUST3.png)
177+
178+
1) First, we check if the query interval is legal.
179+
180+
2) Make some calculation to transform index of input array to the index of tree, their relation is `index(node) = index(input) + leavesCapacity`. And use open interval to describe the query segment.
181+
182+
3) Use bitwise logical operations, we can easily know a node is a leftChild or a rightChild of its parent node. So we can know the query segment fully lie within the right child, or within the left child or none of above. By doing the calqulation, we can know how to combine minimum numbers of nodes value to get final results. For example, query range is `1...4` and input array size is 6. Please refer diagrams above, we only need to combine values of `node9`, `node5` and `node12` to get the result.
183+
184+
This is how you can test it out in a playground:
185+
*/
186+
187+
let array = [1, 2, 3, 4]
188+
189+
var sumSegmentTree = BottomUpSegmentTree(array: array, function: +)
190+
191+
sumSegmentTree.queryWithLeftBound(0, rightBound: 3) // 1 + 2 + 3 + 4 = 10
192+
sumSegmentTree.queryWithLeftBound(1, rightBound: 2) // 2 + 3 = 5
193+
sumSegmentTree.queryWithLeftBound(0, rightBound: 0) // just 1
194+
sumSegmentTree.queryWithLeftBound(3, rightBound: 3) // just 4
195+
//sumSegmentTree.queryWithLeftBound(3, rightBound: 2) // fatal error
196+
197+
sumSegmentTree.queryWithRange(0...3)
198+
sumSegmentTree.queryWithRange(1...2)
199+
sumSegmentTree.queryWithRange(0...0)
200+
sumSegmentTree.queryWithRange(3...3)
201+
//sumSegmentTree.queryWithRange(3...2) // fatal error
202+
/*:
203+
204+
Querying the tree takes **O(log n)** time.
205+
206+
## Replacing items
207+
208+
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. We can append new element as well. A Bottom-up segment tree is very flexible to appending new element. After appending, if the input array size is not exceed `leavesCapacity`, the updating process is as simple as replace a item.
209+
210+
> Note: The tree capacity will neet to enlarge if the input array size is exceed `leavesCapacity`.
211+
212+
Here is the code:
213+
214+
*/
215+
extension BottomUpSegmentTree {
216+
// MARK: - Replace Item
217+
218+
public func replaceItemAtIndex(index: Int, withItem item: T) {
219+
self.result[index + leavesCapacity] = item
220+
var j = (leavesCapacity + index)>>1
221+
while j > 0 {
222+
result[j] = function(result[j<<1], result[j<<1 + 1])
223+
j>>=1
224+
}
225+
}
226+
227+
public func appendArray(newElement: T) {
228+
if arrayCount == leavesCapacity {
229+
var array = [T]()
230+
for i in leavesCapacity..<leavesCapacity<<1 {
231+
array.append(result[i])
232+
}
233+
array.append(newElement)
234+
self.leavesCapacity = leavesCapacityUpdate(array)
235+
self.result = resultUpdate(array)
236+
} else {
237+
replaceItemAtIndex(arrayCount, withItem: newElement)
238+
arrayCount = arrayCount + 1
239+
}
240+
}
241+
}
242+
/*:
243+
Replace and append work with non-recursion.
244+
245+
Replacing an item takes **O(log n)** time.
246+
247+
## Debugging
248+
249+
*/
250+
251+
extension BottomUpSegmentTree: CustomStringConvertible {
252+
// MARK: - Debugging, CustomStringConvertible
253+
254+
public var description: String {
255+
return result.description
256+
}
257+
}
258+
/*:
259+
See the playground for more examples of how to use the segment tree.
260+
261+
## See also
262+
263+
[Bottom-up Segment tree (Chinese)](http://wenku.baidu.com/link?url=57dSmipYHQx56SfyjgSXf62-gsRw7Fmg3xrMjLzdu12LroANGLvCWPUW1kOSFsVrmqfOK64xvmYw8MtZkUX49O27ZupjnBo7CD72I0L2Ou3)
264+
265+
# Tests:
266+
*/
267+
sumSegmentTree = BottomUpSegmentTree(array: [1,2,3,4], function: +)
268+
269+
sumSegmentTree.queryWithLeftBound(0, rightBound: 3) // 1 + 2 + 3 + 4 = 10
270+
sumSegmentTree.queryWithLeftBound(1, rightBound: 2) // 2 + 3 = 5
271+
sumSegmentTree.queryWithLeftBound(0, rightBound: 0) // 1 = 1
272+
sumSegmentTree.replaceItemAtIndex(0, withItem: 5)
273+
sumSegmentTree.appendArray(5)
274+
sumSegmentTree.appendArray(6)
275+
276+
let mulSegmentTree = BottomUpSegmentTree(array: [1,2,3,4,5,6], function: *, associatedtypeNeedInitValue: true)
277+
278+
mulSegmentTree.queryWithLeftBound(0, rightBound: 3) // 1 * 2 * 3 * 4 = 24
279+
mulSegmentTree.queryWithLeftBound(1, rightBound: 2) // 2 * 3 = 6
280+
mulSegmentTree.queryWithLeftBound(2, rightBound: 5) // 3 * 4 * 5 * 6 = 360
281+
mulSegmentTree.replaceItemAtIndex(0, withItem: 1)
282+
mulSegmentTree.appendArray(7)
283+
mulSegmentTree.appendArray(8)
284+
285+
286+
//you can use any associative function (i.e (a+b)+c == a+(b+c)) as function for segment tree
287+
func gcd(m: Int, _ n: Int) -> Int {
288+
var a = 0
289+
var b = max(m, n)
290+
var r = min(m, n)
291+
292+
while r != 0 {
293+
a = b
294+
b = r
295+
r = a % b
296+
}
297+
return b
298+
}
299+
300+
let gcdArray = [2, 4, 6, 3, 5]
301+
302+
let gcdSegmentTree = BottomUpSegmentTree(array: gcdArray, function: gcd)
303+
304+
gcdSegmentTree.queryWithLeftBound(0, rightBound: 1) // gcd(2, 4) = 2
305+
gcdSegmentTree.queryWithLeftBound(2, rightBound: 3) // gcd(6, 3) = 3
306+
gcdSegmentTree.queryWithLeftBound(1, rightBound: 3) // gcd(4, 6, 3) = 1
307+
gcdSegmentTree.queryWithLeftBound(0, rightBound: 4) // gcd(2, 4, 6, 3, 5) = 1
308+
309+
gcdSegmentTree.replaceItemAtIndex(3, withItem: 10) //gcdArray now is [2, 4, 6, 10, 5]
310+
311+
gcdSegmentTree.queryWithLeftBound(3, rightBound: 4) // gcd(10, 5) = 5
312+
313+
314+
//example of segment tree which finds minimum on given range
315+
let minArray = [2, 4, 1, 5, 3]
316+
317+
let minSegmentTree = BottomUpSegmentTree(array: minArray, function: min, associatedtypeInitValue: 100)
318+
319+
minSegmentTree.queryWithLeftBound(0, rightBound: 4) // min(2, 4, 1, 5, 3) = 1
320+
minSegmentTree.queryWithLeftBound(0, rightBound: 1) // min(2, 4) = 2
321+
322+
minSegmentTree.replaceItemAtIndex(2, withItem: 10) // minArray now is [2, 4, 10, 5, 3]
323+
324+
minSegmentTree.queryWithLeftBound(0, rightBound: 4) // min(2, 4, 10, 5, 3) = 2
325+
326+
327+
// type of elements in array can be any type which has some associative function
328+
let stringArray = ["a", "b", "c", "A", "B", "C"]
329+
330+
let stringSegmentTree = BottomUpSegmentTree(array: stringArray, function: +)
331+
332+
stringSegmentTree.queryWithLeftBound(0, rightBound: 1) // "a"+"b" = "ab"
333+
stringSegmentTree.queryWithLeftBound(2, rightBound: 3) // "c"+"A" = "cA"
334+
stringSegmentTree.queryWithLeftBound(1, rightBound: 3) // "b"+"c"+"A" = "bcA"
335+
stringSegmentTree.queryWithLeftBound(0, rightBound: 5) // "a"+"b"+"c"+"A"+"B"+"C" = "abcABC"
336+
337+
stringSegmentTree.replaceItemAtIndex(0, withItem: "I")
338+
stringSegmentTree.replaceItemAtIndex(1, withItem: " like")
339+
stringSegmentTree.replaceItemAtIndex(2, withItem: " algorithms")
340+
stringSegmentTree.replaceItemAtIndex(3, withItem: " and")
341+
stringSegmentTree.replaceItemAtIndex(4, withItem: " swift")
342+
stringSegmentTree.replaceItemAtIndex(5, withItem: "!")
343+
344+
stringSegmentTree.queryWithLeftBound(0, rightBound: 5)
345+
346+
347+
348+
/*:
349+
***
350+
[Previous](@previous) | [Next](@next)
351+
*/
352+
Loading
Loading
Loading

0 commit comments

Comments
 (0)