|
| 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 | + |
| 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 | + |
| 176 | + |
| 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 | + |
0 commit comments