Last active
October 20, 2022 13:46
-
-
Save OleksandrKucherenko/cc03eb23d50cea6048e929ca38fec822 to your computer and use it in GitHub Desktop.
Another solution for old coder task "second unique smallest value" - https://www.geeksforgeeks.org/second-unique-smallest-value-of-given-binary-tree-whose-each-node-is-minimum-of-its-children/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Here's a sample binary tree node class: | |
open class BinaryTreeNode constructor(var value: Int) { | |
var left: BinaryTreeNode = Empty | |
protected set | |
var right: BinaryTreeNode = Empty | |
protected set | |
fun insertLeft(leftValue: Int): BinaryTreeNode { | |
check(leftValue >= 0) { "Expected only positive values" } | |
this.left = BinaryTreeNode(leftValue) | |
this.value = selectMinOrNotFound(left.value, right.value) | |
return this.left | |
} | |
fun insertRight(rightValue: Int): BinaryTreeNode { | |
check(rightValue >= 0) { "Expected only positive values" } | |
this.right = BinaryTreeNode(rightValue) | |
this.value = selectMinOrNotFound(left.value, right.value) | |
return this.right | |
} | |
override fun toString(): String { | |
return "$value [${left.value}, ${right.value}]" | |
} | |
override fun hashCode(): Int = value.hashCode() | |
override fun equals(other: Any?): Boolean = (other is BinaryTreeNode) && value == other.value | |
fun findSecondMin(minOrAvoid: BinaryTreeNode = this): Int = when { | |
isAnyEmpty() -> value.takeIf { it != minOrAvoid.value } ?: NotFound | |
isInRange(minOrAvoid, maxNode()) -> value | |
else -> selectMinOrNotFound(left.findSecondMin(minOrAvoid), right.findSecondMin(minOrAvoid)) | |
} | |
companion object { | |
const val NotFound: Int = -1; | |
val Empty = object : BinaryTreeNode(NotFound) { | |
init { | |
this.left = this; | |
this.right = this; | |
} | |
} | |
} | |
} | |
operator fun BinaryTreeNode.compareTo(other: BinaryTreeNode): Int = this.value.compareTo(other.value) | |
fun BinaryTreeNode.isAnyEmpty(): Boolean { | |
return (this == BinaryTreeNode.Empty || this.left === BinaryTreeNode.Empty || this.right === BinaryTreeNode.Empty) | |
} | |
fun BinaryTreeNode.isInRange(min: BinaryTreeNode, max: BinaryTreeNode): Boolean = (min < this && this < max) | |
fun BinaryTreeNode.maxNode(): BinaryTreeNode = if (left > right) left else right | |
fun selectMinOrNotFound(first: Int, second: Int): Int = | |
arrayOf(first, second).filter { it > BinaryTreeNode.NotFound }.sorted() | |
.elementAtOrElse(0) { BinaryTreeNode.NotFound } | |
/** ref: https://stackoverflow.com/questions/4965335/how-to-print-binary-tree-diagram-in-java */ | |
fun BinaryTreeNode.printTree(prefix: String = "", isLeft: Boolean = false): String { | |
if(this == BinaryTreeNode.Empty) return ""; | |
/* box symbols - ref: https://gist.github.com/dsample/79a97f38bf956f37a0f99ace9df367b9 */ | |
val (nodeLine, connectorLine) = (if (isLeft) "├──" to "│ " else "└──" to " ") | |
val nodes = "${prefix}$nodeLine" | |
val branches = "${prefix}$connectorLine " | |
return "$nodes $this\n" + left.printTree(branches, true) + right.printTree(branches) | |
} | |
/* ------------------------------------------ */ | |
/** Data for testing. */ | |
object Data { | |
val simple1 = BinaryTreeNode(2) | |
val simple2 = BinaryTreeNode(2).apply { insertLeft(2); insertRight(5) }; | |
val example1 = BinaryTreeNode(2).apply { | |
insertLeft(2) | |
insertRight(5).apply { | |
insertLeft(5) | |
insertRight(7) | |
} | |
} | |
val example2 = BinaryTreeNode(2).apply { | |
insertLeft(2) | |
insertRight(2) | |
} | |
val example3 = BinaryTreeNode(2).apply { | |
insertLeft(2).apply { | |
insertLeft(2) | |
insertRight(5) | |
} | |
insertRight(3).apply { | |
insertLeft(3) | |
insertRight(5) | |
} | |
} | |
val example4 = BinaryTreeNode(2).apply { | |
insertLeft(2).apply { | |
insertLeft(2).apply { | |
insertLeft(2) | |
insertRight(4) | |
} | |
insertRight(5) | |
} | |
insertRight(2).apply { | |
insertLeft(2).apply { | |
insertLeft(2) | |
insertRight(3) | |
} | |
insertRight(5) | |
} | |
} | |
} | |
/** Verify function. */ | |
fun verify(function: (BinaryTreeNode) -> Int) { | |
check(-1 == function(Data.simple1)) { "expected -1 for ${Data.simple1.printTree()}" } | |
check(5 == function(Data.simple2)) { "expected 5 for ${Data.simple2.printTree()}" } | |
check(5 == function(Data.example1)) { "expected 5 for ${Data.example1.printTree()}" } | |
check(-1 == function(Data.example2)) { "expected -1 for ${Data.example2.printTree()}" } | |
check(3 == function(Data.example3)) { "expected 3 for ${Data.example3.printTree()}" } | |
check(3 == function(Data.example4)) { "expected 3 for ${Data.example4.printTree()}" } | |
} | |
fun main() { | |
verify { println(it.printTree()); it.findSecondMin() } | |
} |
Version 3:
fun secondMinV3(root: BinaryTreeNode, parentMin: BinaryTreeNode = root): Int {
val left = root.left!!
val right = root.right!!
val higher = if (left.value > right.value) left else right
return when {
root.isEmpty() -> arrayOf(root.valueOrElseOnEquals(parentMin) { BinaryTreeNode.NotFound })
root.hasValueOf(parentMin) -> arrayOf(secondMinV3(left, parentMin), secondMinV3(right, parentMin))
root.inRange(parentMin, higher) -> arrayOf(root.value)
else -> arrayOf(secondMinV3(higher, parentMin))
}.filter { it > BinaryTreeNode.NotFound }.sorted().elementAtOrElse(0) { BinaryTreeNode.NotFound }
}
removed NULLs:
fun secondMinV3(root: BinaryTreeNode, parentMin: BinaryTreeNode = root): Int {
val higher = if (root.left.value > root.right.value) root.left else root.right
return when {
root.isEmpty() -> arrayOf(root.onEqualsDoElse(parentMin) { BinaryTreeNode.NotFound })
root.hasValueOf(parentMin) -> arrayOf(secondMinV3(root.left, parentMin), secondMinV3(root.right, parentMin))
root.inRange(parentMin, higher) -> arrayOf(root.value)
else -> arrayOf(secondMinV3(higher, parentMin))
}.filter { it > BinaryTreeNode.NotFound }.sorted().elementAtOrElse(0) { BinaryTreeNode.NotFound }
}
Version 5:
class BinaryTreeNode {
/* ... */
fun findSecondMin(min: BinaryTreeNode = this): Int = when {
isEmpty() -> valueOrElse(min) { NotFound }
isInRange(min, maxNode()) -> value
hasNodeWithValue(min) -> result(left.findSecondMin(min), right.findSecondMin(min))
else -> throw RuntimeException("Should never happens")
}
/* ... */
}
Version 6: better naming
class BinaryTreeNode {
/* ... */
fun findSecondMin(minOrAvoid: BinaryTreeNode = this): Int = when {
isAnyEmpty() -> value.takeIf { it != minOrAvoid.value } ?: NotFound
isInRange(minOrAvoid, maxNode()) -> value
else -> selectMinOrNotFound(left.findSecondMin(minOrAvoid), right.findSecondMin(minOrAvoid))
}
/* ... */
}
Samples/Testing Data:
└── 2 [-1, -1]
└── 2 [2, 5]
├── 2 [-1, -1]
└── 5 [-1, -1]
└── 2 [2, 5]
├── 2 [-1, -1]
└── 5 [5, 7]
├── 5 [-1, -1]
└── 7 [-1, -1]
└── 2 [2, 2]
├── 2 [-1, -1]
└── 2 [-1, -1]
└── 2 [2, 3]
├── 2 [2, 5]
│ ├── 2 [-1, -1]
│ └── 5 [-1, -1]
└── 3 [3, 5]
├── 3 [-1, -1]
└── 5 [-1, -1]
└── 2 [2, 2]
├── 2 [2, 5]
│ ├── 2 [2, 4]
│ │ ├── 2 [-1, -1]
│ │ └── 4 [-1, -1]
│ └── 5 [-1, -1]
└── 2 [2, 5]
├── 2 [2, 3]
│ ├── 2 [-1, -1]
│ └── 3 [-1, -1]
└── 5 [-1, -1]
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Version 1: