Skip to content

Commit d613971

Browse files
committed
update BigTextImpl to use new multiplatform red-black tree. Only BigTextImplTest and RedBlackTreeTest are passed
1 parent f7885f0 commit d613971

9 files changed

Lines changed: 722 additions & 261 deletions

File tree

datastructure/src/commonMain/kotlin/com/sunnychung/lib/multiplatform/bigtext/redblacktree/RedBlackTree.kt

Lines changed: 114 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,46 @@
11
package com.sunnychung.lib.multiplatform.bigtext.redblacktree
22

3-
private enum class Color { RED, BLACK }
3+
enum class Color { RED, BLACK }
44

5-
open class RedBlackTree<K : Comparable<K>, V> {
6-
private inner class Node(
7-
var key: K,
8-
var value: V,
9-
var color: Color,
10-
var left: Node? = null,
11-
var right: Node? = null,
12-
var parent: Node? = null
13-
)
5+
open class RedBlackTree<K : Comparable<K>, V> : Iterable<V> {
6+
inner class Node(
7+
key: K,
8+
value: V,
9+
color: Color,
10+
left: Node? = null,
11+
right: Node? = null,
12+
parent: Node? = null
13+
) {
14+
var parent = parent
15+
internal set
16+
var right = right
17+
internal set
18+
var left = left
19+
internal set
20+
var color = color
21+
internal set
22+
var value = value
23+
internal set
24+
var key = key
25+
internal set
26+
}
1427

15-
private var root: Node? = null
28+
var root: Node? = null
29+
protected set
1630

1731
// --- Public API ---
1832

33+
val isEmpty: Boolean
34+
get() = root == null
35+
36+
var size: Int = 0
37+
protected set
38+
1939
fun get(key: K): V? = searchNode(key)?.value
2040

2141
fun contains(key: K): Boolean = searchNode(key) != null
2242

23-
fun insert(key: K, value: V) {
43+
open fun insert(key: K, value: V): Node? {
2444
var y: Node? = null
2545
var x = root
2646

@@ -31,7 +51,7 @@ open class RedBlackTree<K : Comparable<K>, V> {
3151
key > x.key -> x = x.right
3252
else -> {
3353
x.value = value
34-
return
54+
return null
3555
}
3656
}
3757
}
@@ -46,6 +66,8 @@ open class RedBlackTree<K : Comparable<K>, V> {
4666
}
4767

4868
insertFixup(z)
69+
++size
70+
return z
4971
}
5072

5173
fun remove(key: K) {
@@ -59,6 +81,58 @@ open class RedBlackTree<K : Comparable<K>, V> {
5981
return result
6082
}
6183

84+
fun findNode(comparison: (Node) -> Int): Node? {
85+
var x = root
86+
while (x != null) {
87+
val comparisonResult = comparison(x)
88+
when {
89+
comparisonResult < 0 -> x = x.left
90+
comparisonResult > 0 -> x = x.right
91+
else -> return x
92+
}
93+
}
94+
return null
95+
}
96+
97+
fun find(comparison: (Node) -> Int): V? = findNode(comparison)?.value
98+
99+
override fun iterator(): Iterator<V> = object : Iterator<V> {
100+
private val stack = mutableListOf<RedBlackTree<K, V>.Node>()
101+
private var current = root
102+
103+
init {
104+
// Push all the way to the left
105+
while (current != null) {
106+
stack.add(current!!)
107+
current = current!!.left
108+
}
109+
current = null
110+
}
111+
112+
override fun hasNext(): Boolean = stack.isNotEmpty()
113+
114+
override fun next(): V {
115+
if (!hasNext()) throw NoSuchElementException()
116+
val node = stack.removeAt(stack.lastIndex)
117+
var nextNode = node.right
118+
while (nextNode != null) {
119+
stack.add(nextNode)
120+
nextNode = nextNode.left
121+
}
122+
return node.value
123+
}
124+
}
125+
126+
fun visitInPostOrder(visitor: (Node) -> Unit) {
127+
fun visit(node: Node?) {
128+
if (node == null) return
129+
visit(node.left)
130+
visit(node.right)
131+
visitor(node)
132+
}
133+
visit(root)
134+
}
135+
62136
// --- Internal helpers ---
63137

64138
private fun inOrder(node: Node?, list: MutableList<Pair<K, V>>) {
@@ -81,7 +155,7 @@ open class RedBlackTree<K : Comparable<K>, V> {
81155
return null
82156
}
83157

84-
private fun insertFixup(z: Node) {
158+
protected fun insertFixup(z: Node) {
85159
var z = z
86160
while (z.parent?.color == Color.RED) {
87161
val p = z.parent!!
@@ -123,7 +197,8 @@ open class RedBlackTree<K : Comparable<K>, V> {
123197
root?.color = Color.BLACK
124198
}
125199

126-
private fun transplant(u: Node, v: Node?) {
200+
protected open fun transplant(u: Node, v: Node?) {
201+
val uParent = u.parent
127202
if (u.parent == null) {
128203
root = v
129204
} else if (u == u.parent?.left) {
@@ -132,6 +207,8 @@ open class RedBlackTree<K : Comparable<K>, V> {
132207
u.parent?.right = v
133208
}
134209
v?.parent = u.parent
210+
uParent?.let { n -> recompute(n) }
211+
// the recomputation of `v` is called outside
135212
}
136213

137214
private fun minimum(x: Node): Node {
@@ -140,7 +217,7 @@ open class RedBlackTree<K : Comparable<K>, V> {
140217
return curr
141218
}
142219

143-
private fun deleteNode(z: Node) {
220+
open fun deleteNode(z: Node) {
144221
var y = z
145222
var yOriginalColor = y.color
146223
var x: Node? = null
@@ -150,10 +227,12 @@ open class RedBlackTree<K : Comparable<K>, V> {
150227
x = z.right
151228
xParent = z.parent
152229
transplant(z, z.right)
230+
xParent?.let { n -> recomputeBottomUp(n) }
153231
} else if (z.right == null) {
154232
x = z.left
155233
xParent = z.parent
156234
transplant(z, z.left)
235+
xParent?.let { n -> recomputeBottomUp(n) }
157236
} else {
158237
y = minimum(z.right!!)
159238
yOriginalColor = y.color
@@ -170,13 +249,26 @@ open class RedBlackTree<K : Comparable<K>, V> {
170249
y.left = z.left
171250
y.left?.parent = y
172251
y.color = z.color
252+
253+
recomputeBottomUp(y)
173254
}
174255

175256
if (yOriginalColor == Color.BLACK) {
176257
deleteFixup(x, xParent)
177258
}
259+
--size
178260
}
179261

262+
protected open fun recomputeBottomUp(n: Node) {
263+
var p: Node? = n
264+
while (p != null) {
265+
recompute(p)
266+
p = p.parent
267+
}
268+
}
269+
270+
protected open fun recompute(n: Node) = Unit
271+
180272
/**
181273
* x is the replacement node (may be null)
182274
* p is the parent of x (may be null if x == root)
@@ -248,8 +340,8 @@ open class RedBlackTree<K : Comparable<K>, V> {
248340
x?.color = Color.BLACK
249341
}
250342

251-
private fun rotateLeft(x: Node) {
252-
val y = x.right ?: return
343+
protected open fun rotateLeft(x: Node): Boolean {
344+
val y = x.right ?: return false
253345
x.right = y.left
254346
if (y.left != null) y.left?.parent = x
255347
y.parent = x.parent
@@ -262,10 +354,11 @@ open class RedBlackTree<K : Comparable<K>, V> {
262354
}
263355
y.left = x
264356
x.parent = y
357+
return true
265358
}
266359

267-
private fun rotateRight(x: Node) {
268-
val y = x.left ?: return
360+
protected open fun rotateRight(x: Node): Boolean {
361+
val y = x.left ?: return false
269362
x.left = y.right
270363
if (y.right != null) y.right?.parent = x
271364
y.parent = x.parent
@@ -278,6 +371,7 @@ open class RedBlackTree<K : Comparable<K>, V> {
278371
}
279372
y.right = x
280373
x.parent = y
374+
return true
281375
}
282376

283377
// --- For Testing Only ---

datastructure/src/commonTest/kotlin/com/sunnychung/lib/multiplatform/bigtext/redblacktree/RedBlackTreeTest.kt

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,4 +294,43 @@ class RedBlackTreeTest {
294294
assertTreeState(tree, expectedStates.getOrElse(i) { expectedStates.last() }.sortedBy { it.first })
295295
}
296296
}
297+
298+
@Test
299+
fun testIteratorInOrder() {
300+
val tree = RedBlackTree<Int, String>()
301+
val keys = listOf(10, 4, 15, 2, 7)
302+
for (k in keys) tree.insert(k, "v$k")
303+
val expected = keys.sorted().map { "v$it" }
304+
val iterated = tree.toList()
305+
assertEquals(expected, iterated)
306+
}
307+
308+
@Test
309+
fun testIteratorEmptyTree() {
310+
val tree = RedBlackTree<Int, String>()
311+
assertFalse(tree.iterator().hasNext())
312+
assertEquals(emptyList(), tree.toList())
313+
}
314+
315+
@Test
316+
fun testIteratorAfterDelete() {
317+
val tree = RedBlackTree<Int, String>()
318+
for (k in 1..5) tree.insert(k, "v$k")
319+
tree.remove(3)
320+
val expected = listOf("v1", "v2", "v4", "v5")
321+
assertEquals(expected, tree.toList())
322+
}
323+
324+
@Test
325+
fun testForEachLoop() {
326+
val tree = RedBlackTree<Int, String>()
327+
val keys = listOf(8, 3, 10, 1, 6)
328+
for (k in keys) tree.insert(k, "v$k")
329+
val result = mutableListOf<String>()
330+
for (v in tree) {
331+
result.add(v)
332+
}
333+
val expected = keys.sorted().map { "v$it" }
334+
assertEquals(expected, result)
335+
}
297336
}

0 commit comments

Comments
 (0)