From 765c52a220d539b22df2b163c62175d91a8125ad Mon Sep 17 00:00:00 2001 From: norangebit Date: Sun, 9 Jun 2019 12:44:26 +0200 Subject: [PATCH] add exercise with tree - add descendants of a node - add distance to leaf - add max width of a tree --- .../algorithms/exam/DescendentsTree.kt | 99 ++++++++++++++ .../algorithms/exam/DistanceToLeafTree.kt | 114 ++++++++++++++++ .../norangeb/algorithms/exam/MaxWidthTree.kt | 111 ++++++++++++++++ .../algorithms/exam/DescendentsTest.kt | 124 ++++++++++++++++++ .../algorithms/exam/DistanceToLeafTest.kt | 108 +++++++++++++++ .../algorithms/exam/MaxWidthTreeTest.kt | 112 ++++++++++++++++ 6 files changed, 668 insertions(+) create mode 100644 src/main/kotlin/it/norangeb/algorithms/exam/DescendentsTree.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/exam/DistanceToLeafTree.kt create mode 100644 src/main/kotlin/it/norangeb/algorithms/exam/MaxWidthTree.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/exam/DescendentsTest.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/exam/DistanceToLeafTest.kt create mode 100644 src/test/kotlin/it/norangeb/algorithms/exam/MaxWidthTreeTest.kt diff --git a/src/main/kotlin/it/norangeb/algorithms/exam/DescendentsTree.kt b/src/main/kotlin/it/norangeb/algorithms/exam/DescendentsTree.kt new file mode 100644 index 0000000..d837c08 --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/exam/DescendentsTree.kt @@ -0,0 +1,99 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.exam + +import arrow.core.None +import arrow.core.Option +import arrow.core.toOption + +class DescendentsTree, V> { + + private var root: Option> = None + + operator fun get(key: K): Option = get(root, key).map { it.value } + + private fun get( + node: Option>, + key: K + ): Option> = node.flatMap { + when { + key == it.key -> node + key > it.key -> get(it.right, key) + else -> get(it.left, key) + } + } + + operator fun set(key: K, value: V) { + root = set(root, key, value) + } + + private fun set( + node: Option>, + key: K, + value: V + ): Option> = node.fold( + { Node(key, value) }, + { + when { + key == it.key -> it.clone(value = value) + key > it.key -> it.clone(right = set(it.right, key, value)) + else -> it.clone(left = set(it.left, key, value)) + } + } + ).toOption() + + fun descendents(key: K): Option { + val parent = get(root, key) + var descendents = 0 + + return parent.map { + inOrder(parent) { descendents++ } + descendents - 1 + } + } + + private fun inOrder(node: Option>, action: (V) -> R) { + node.map { + inOrder(it.left, action) + action(it.value) + inOrder(it.right, action) + } + } + + data class Node( + val key: K, + val value: V, + val left: Option> = None, + val right: Option> = None + ) { + fun clone( + key: K = this.key, + value: V = this.value, + left: Option> = this.left, + right: Option> = this.right + ): Node = Node(key, value, left, right) + } +} diff --git a/src/main/kotlin/it/norangeb/algorithms/exam/DistanceToLeafTree.kt b/src/main/kotlin/it/norangeb/algorithms/exam/DistanceToLeafTree.kt new file mode 100644 index 0000000..0ff5a04 --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/exam/DistanceToLeafTree.kt @@ -0,0 +1,114 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.exam + +import arrow.core.None +import arrow.core.Option +import arrow.core.toOption +import it.norangeb.algorithms.datastructures.queue.Queue +import it.norangeb.algorithms.datastructures.queue.ResizingArrayQueue +import kotlin.math.abs + +class DistanceToLeafTree, V> { + + private var root: Option> = None + + operator fun set(key: K, value: V) { + root = set(key, value, root) + } + + private fun set( + key: K, + value: V, + node: Option> + ): Option> = node.fold( + { Node(key, value) }, + { + when { + key == it.key -> it.clone(value = value) + key > it.key -> it.clone(right = set(key, value, it.right)) + else -> it.clone(left = set(key, value, it.left)) + } + }).toOption() + + operator fun get(key: K): Option = get(key, root).map { it.value } + + private fun get( + key: K, + node: Option> + ): Option> = node.flatMap { + when { + key == it.key -> node + key > it.key -> get(key, it.right) + else -> get(key, it.left) + } + } + + fun distanceToLeaf(key: K): Option = get(key, root).map { + if (it.isLeaf()) + 0 + else + distanceToLeaf(it) + } + + private fun distanceToLeaf(node: Node): Int { + val toExplore: Queue, Int>> = ResizingArrayQueue() + node.left.map { toExplore.enqueue(Pair(it, 1)) } + node.right.map { toExplore.enqueue(Pair(it, 1)) } + + while (!toExplore.isEmpty()) { + toExplore.dequeue().map { elem -> + if (elem.first.isLeaf()) + return elem.second + + elem.first.left.map { + toExplore.enqueue(Pair(it, elem.second + 1)) + } + elem.first.right.map { + toExplore.enqueue(Pair(it, elem.second + 1)) + } + } + } + + return -1 + } + + private data class Node( + val key: K, + val value: V, + val left: Option> = None, + val right: Option> = None + ) { + fun clone( + key: K = this.key, + value: V = this.value, + left: Option> = this.left, + right: Option> = this.right + ) = Node(key, value, left, right) + + fun isLeaf() = left == None && right == None + } +} \ No newline at end of file diff --git a/src/main/kotlin/it/norangeb/algorithms/exam/MaxWidthTree.kt b/src/main/kotlin/it/norangeb/algorithms/exam/MaxWidthTree.kt new file mode 100644 index 0000000..26d6465 --- /dev/null +++ b/src/main/kotlin/it/norangeb/algorithms/exam/MaxWidthTree.kt @@ -0,0 +1,111 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.exam + +import arrow.core.None +import arrow.core.Option +import arrow.core.toOption +import kotlin.math.abs + +class MaxWidthTree, V> { + + private var root: Option> = None + + operator fun set(key: K, value: V) { + root = set(key, value, root) + } + + private fun set( + key: K, + value: V, + node: Option> + ): Option> = node.fold( + { Node(key, value) }, + { + when { + key == it.key -> it.clone(value = value) + key > it.key -> it.clone(right = set(key, value, it.right)) + else -> it.clone(left = set(key, value, it.left)) + } + }).toOption() + + operator fun get(key: K): Option = get(key, root).map { it.value } + + private fun get( + key: K, + node: Option> + ): Option> = node.flatMap { + when { + key == it.key -> node + key > it.key -> get(key, it.right) + else -> get(key, it.left) + } + } + + fun getMaxWidth(): Int { + val count = Array(100) { 0 } + val level = 0 + computeWidth(root, count, level) + + return count[getMax(count)] + } + + private fun computeWidth( + node: Option>, + count: Array, + level: Int + ) { + node.map { + count[level]++ + computeWidth(it.left, count, level + 1) + computeWidth(it.right, count, level + 1) + } + } + + private fun getMax(count: Array): Int { + var maxIndex = 0 + count.forEachIndexed { index, i -> + if (i > count[maxIndex]) + maxIndex = index + } + + return maxIndex + } + + private data class Node( + val key: K, + val value: V, + val left: Option> = None, + val right: Option> = None + ) { + fun clone( + key: K = this.key, + value: V = this.value, + left: Option> = this.left, + right: Option> = this.right + ) = Node(key, value, left, right) + } +} \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/exam/DescendentsTest.kt b/src/test/kotlin/it/norangeb/algorithms/exam/DescendentsTest.kt new file mode 100644 index 0000000..9345bd9 --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/exam/DescendentsTest.kt @@ -0,0 +1,124 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.exam + +import arrow.core.None +import arrow.core.Option +import arrow.core.Some +import org.amshove.kluent.`should equal` +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.gherkin.Feature + +object DescendentsTest : Spek({ + Feature("descendents") { + Scenario("get and set") { + val tree by memoized { DescendentsTree() } + + When("add couple 15-15, 13-13, 23-23, 5-5") { + tree[15] = 15 + tree[13] = 13 + tree[23] = 24 + tree[5] = 5 + } + + Then("tree of 23 should equal 24") { + tree[23] `should equal` Some(24) + } + + Then("tree of 25 should equal None") { + tree[25] `should equal` None + } + + Then("tree of 5 should equal 5") { + tree[5] `should equal` Some(5) + } + + When("add couple 1-1, 17-17, 25-25, 23-23") { + tree[1] = 1 + tree[17] = 17 + tree[25] = 25 + tree[23] = 23 + } + + Then("tree of 23 should equal 23") { + tree[23] `should equal` Some(23) + } + + Then("tree of 25 should equal 25") { + tree[25] `should equal` Some(25) + } + } + + Scenario("descendents") { + val tree by memoized { DescendentsTree() } + + Given("tree 15, 23, 7, 18, 25, 21, 22, 20") { + tree[15] = 15 + tree[23] = 23 + tree[7] = 7 + tree[18] = 18 + tree[25] = 25 + tree[21] = 21 + tree[22] = 22 + tree[20] = 20 + } + + lateinit var result: Option + + When("compute descendents of 21") { + result = tree.descendents(21) + } + + Then("result should equal 2") { + result `should equal` Some(2) + } + + When("compute descendents of 7") { + result = tree.descendents(7) + } + + Then("result should equal 0") { + result `should equal` Some(0) + } + + When("compute descendents of 23") { + result = tree.descendents(23) + } + + Then("result should equal 5") { + result `should equal` Some(5) + } + + When("compute descendents of 8") { + result = tree.descendents(8) + } + + Then("result should equal None") { + result `should equal` None + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/exam/DistanceToLeafTest.kt b/src/test/kotlin/it/norangeb/algorithms/exam/DistanceToLeafTest.kt new file mode 100644 index 0000000..f8f5834 --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/exam/DistanceToLeafTest.kt @@ -0,0 +1,108 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.exam + +import arrow.core.None +import arrow.core.Some +import org.amshove.kluent.`should equal` +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.gherkin.Feature + +object DistanceToLeafTest : Spek({ + Feature("distance to leaf") { + Scenario("get and set") { + val tree by memoized { DistanceToLeafTree() } + + When("add couple 15-15, 13-13, 23-23, 5-5") { + tree[15] = 15 + tree[13] = 13 + tree[23] = 24 + tree[5] = 5 + } + + Then("tree of 23 should equal 24") { + tree[23] `should equal` Some(24) + } + + Then("tree of 25 should equal None") { + tree[25] `should equal` None + } + + Then("tree of 5 should equal 5") { + tree[5] `should equal` Some(5) + } + + When("add couple 1-1, 17-17, 25-25, 23-23") { + tree[1] = 1 + tree[17] = 17 + tree[25] = 25 + tree[23] = 23 + } + + Then("tree of 23 should equal 23") { + tree[23] `should equal` Some(23) + } + + Then("tree of 25 should equal 25") { + tree[25] `should equal` Some(25) + } + } + + Scenario("distance to leaf") { + val tree by memoized { DistanceToLeafTree() } + + Given("") { + tree[18] = 18 + tree[15] = 15 + tree[23] = 23 + tree[7] = 7 + tree[17] = 17 + tree[3] = 3 + tree[10] = 10 + tree[1] = 1 + tree[4] = 4 + tree[12] = 12 + tree[14] = 14 + } + + Then("distance to leaf of 25 should equal None") { + tree.distanceToLeaf(25) `should equal` None + } + + Then("distance to leaf of 15 should equal 1") { + tree.distanceToLeaf(15) `should equal` Some(1) + } + + Then("distance to leaf of 7 should equal 2") { + tree.distanceToLeaf(7) `should equal` Some(2) + } + + Then("distance to leaf of 4 should equal 0") { + tree.distanceToLeaf(4) `should equal` Some(0) + } + } + } +}) \ No newline at end of file diff --git a/src/test/kotlin/it/norangeb/algorithms/exam/MaxWidthTreeTest.kt b/src/test/kotlin/it/norangeb/algorithms/exam/MaxWidthTreeTest.kt new file mode 100644 index 0000000..2441695 --- /dev/null +++ b/src/test/kotlin/it/norangeb/algorithms/exam/MaxWidthTreeTest.kt @@ -0,0 +1,112 @@ +/* + * MIT License + * + * Copyright (c) 2019 norangebit + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +package it.norangeb.algorithms.exam + +import arrow.core.None +import arrow.core.Option +import arrow.core.Some +import org.amshove.kluent.`should be equal to` +import org.amshove.kluent.`should equal` +import org.spekframework.spek2.Spek +import org.spekframework.spek2.style.gherkin.Feature + +object MaxWidthTreeTest : Spek({ + Feature("width") { + Scenario("get and set") { + val tree by memoized { MaxWidthTree() } + + When("add couple 15-15, 13-13, 23-23, 5-5") { + tree[15] = 15 + tree[13] = 13 + tree[23] = 24 + tree[5] = 5 + } + + Then("tree of 23 should equal 24") { + tree[23] `should equal` Some(24) + } + + Then("tree of 25 should equal None") { + tree[25] `should equal` None + } + + Then("tree of 5 should equal 5") { + tree[5] `should equal` Some(5) + } + + When("add couple 1-1, 17-17, 25-25, 23-23") { + tree[1] = 1 + tree[17] = 17 + tree[25] = 25 + tree[23] = 23 + } + + Then("tree of 23 should equal 23") { + tree[23] `should equal` Some(23) + } + + Then("tree of 25 should equal 25") { + tree[25] `should equal` Some(25) + } + } + + Scenario("width") { + val tree by memoized { MaxWidthTree() } + + var width = -1 + + Then("width should equal 0") { + tree.getMaxWidth() `should be equal to` 0 + } + + Given("tree 15, 23, 7, 18, 25, 21, 22, 20") { + tree[15] = 15 + tree[23] = 23 + tree[7] = 7 + tree[18] = 18 + tree[25] = 25 + tree[21] = 21 + tree[22] = 22 + tree[20] = 20 + tree[10] = 10 + } + + Then("width should equal 3") { + tree.getMaxWidth() `should be equal to` 3 + } + + When("add 27, 26, 28") { + tree[27] = 27 + tree[26] = 26 + tree[28] = 28 + } + + Then("width should equal 4") { + tree.getMaxWidth() `should be equal to` 4 + } + } + } +}) \ No newline at end of file