Skip to content

Commit

Permalink
Merge pull request #4 from spbu-coding-2023/test
Browse files Browse the repository at this point in the history
Merge branches test and main
  • Loading branch information
IliaSuponeff authored Mar 31, 2024
2 parents 7975b78 + 8ff064e commit 5e78d5c
Show file tree
Hide file tree
Showing 5 changed files with 91 additions and 141 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,4 @@ jobs:
- uses: actions/checkout@v3

- name: Build source code and run test with jacoco
run: ./gradlew :test
run: ./gradlew test
145 changes: 62 additions & 83 deletions lib/src/main/kotlin/tree_tripper/binary_trees/RBTree.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,36 +41,74 @@ public open class RBTree<K: Comparable<K>, V>: AbstractBSTree<K, V, RBTreeNode<K

val removeResult: Pair<RBTreeNode<K, V>?, V?>
val resultCompare: Int = key.compareTo(node.key)
var nodeCurrent: RBTreeNode<K, V> = node
val nodeCurrent: RBTreeNode<K, V> = node
if (resultCompare < 0) {
if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild))
nodeCurrent = moveRedLeft(nodeCurrent)

removeResult = removeNode(nodeCurrent.leftChild, key)
nodeCurrent.leftChild = removeResult.first
} else if (resultCompare > 0) {
removeResult = removeNode(nodeCurrent.rightChild, key)
nodeCurrent.rightChild = removeResult.first
} else {
if (isRedColor(nodeCurrent.leftChild))
nodeCurrent = rotateRight(nodeCurrent)
if (resultCompare == 0 && nodeCurrent.rightChild == null)
val leftChild = nodeCurrent.leftChild
val rightChild = nodeCurrent.rightChild
if (leftChild == null && rightChild == null) {
if (isRedColor(nodeCurrent)) return Pair(null, nodeCurrent.value)
if (isRedColor(nodeCurrent.parent)) {
flipColors(nodeCurrent.parent)
} else {
val uncle = nodeCurrent.getUncle()
if (isRedColor(uncle)) {
flipColors(uncle)
nodeCurrent.parent?.flipColor()
}
uncle?.flipColor()
}
return Pair(null, nodeCurrent.value)
if (!isRedColor(nodeCurrent.rightChild) && !isRedLeftChild(nodeCurrent.rightChild))
nodeCurrent = moveRedRight(nodeCurrent)
if (resultCompare == 0) {
val nodeWithMinimalKey = getMinNodeInSubtree(nodeCurrent.rightChild) as RBTreeNode
val nodeSubstitutive: RBTreeNode<K, V> = createNode(nodeWithMinimalKey.key, nodeWithMinimalKey.value)
nodeSubstitutive.isRed = nodeCurrent.isRed
nodeSubstitutive.leftChild = nodeCurrent.leftChild
nodeSubstitutive.rightChild = removeMinNode(nodeCurrent.rightChild)
return Pair(balanceTree(nodeSubstitutive), nodeCurrent.value)
} else if (leftChild == null) {
throw IllegalArgumentException(
"Invalid RedBlackTree state, node with one child as right is not valid rb-tree"
)
} else if (rightChild == null) {
if (isRedColor(leftChild)) {
flipColors(nodeCurrent)
return Pair(leftChild, nodeCurrent.value)
}
throw IllegalArgumentException(
"Invalid RedBlackTree state, node with one child as left with black color is not valid rb-tree"
)
} else {
removeResult = removeNode(nodeCurrent.rightChild, key)
nodeCurrent.rightChild = removeResult.first
val newNode: RBTreeNode<K, V>
val nodeCached: RBTreeNode<K, V>
if (isRedColor(nodeCurrent)) {
nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode<K, V>
newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCached.isRed)
newNode.leftChild = leftChild
newNode.rightChild = removeNode(rightChild, nodeCached.key).first
if (!isRedColor(nodeCached)) leftChild.flipColor()
} else {
if (isRedColor(leftChild)) {
nodeCached = getMaxNodeInSubtree(leftChild) as RBTreeNode<K, V>
newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCurrent.isRed)
newNode.rightChild = rightChild
newNode.leftChild = removeNode(leftChild, nodeCached.key).first
return Pair(balanceTree(newNode), nodeCurrent.value)
}
nodeCached = getMinNodeInSubtree(rightChild) as RBTreeNode<K, V>
newNode = RBTreeNode(nodeCached.key, nodeCached.value, nodeCurrent.isRed)
newNode.leftChild = leftChild
newNode.rightChild = removeNode(rightChild, nodeCached.key).first
if (!isRedColor(nodeCached)) {
leftChild.isRed = true
newNode.isRed = true
}
}
return Pair(balanceTree(newNode), nodeCurrent.value)
}
}

return Pair(balanceTree(nodeCurrent), removeResult.second)
}

/**
* Returns whether the specified node is red or not.
*
Expand Down Expand Up @@ -132,70 +170,11 @@ public open class RBTree<K: Comparable<K>, V>: AbstractBSTree<K, V, RBTreeNode<K
*
* @param node needed to flip the colors
*/
protected fun flipColors(node: RBTreeNode<K, V>): Unit {
node.isRed = !node.isRed
notNullNodeUpdate(node.leftChild) { child -> child.isRed = !child.isRed }
notNullNodeUpdate(node.rightChild) { child -> child.isRed = !child.isRed }
}

/**
* This function is used to move a red node to the right, if it has a red left child of its [node] left child.
* It first flips the colors of the node and its children, then rotates the tree if the left child is also red.
*
* @param node the node to move
* @return the new root of the tree, which is balanced node subtree
*/
private fun moveRedRight(node: RBTreeNode<K, V>): RBTreeNode<K, V> {
var nodeCurrent: RBTreeNode<K, V> = node

flipColors(nodeCurrent)
if (isRedLeftChild(nodeCurrent.leftChild)) {
nodeCurrent = rotateRight(nodeCurrent)
flipColors(nodeCurrent)
}
return nodeCurrent
}

/**
* This function is used to move a red node to the left, if it has a red right child of its [node] left child.
* It first flips the colors of the node and its children, then rotates the tree if the left child is also red.
*
* @param node the node to move
* @return the new root of the tree, which is balanced node subtree
*/
private fun moveRedLeft(node: RBTreeNode<K, V>): RBTreeNode<K, V> {
var nodeCurrent: RBTreeNode<K, V> = node

flipColors(nodeCurrent)
if (isRedLeftChild(nodeCurrent.rightChild)) {
nodeCurrent.rightChild = notNullNodeAction(
node.rightChild, null
) {rightChild -> rotateRight(rightChild)}
nodeCurrent = rotateLeft(nodeCurrent)
flipColors(nodeCurrent)
}
return nodeCurrent
}

/**
* Removes the node with the minimum key from the binary search tree.
*
* @param node the root of the binary search tree
* @return the root of the binary search tree with the node removed, or `null` if the tree is empty
*/
private fun removeMinNode(node: RBTreeNode<K, V>?): RBTreeNode<K, V>? {
if (node == null) return null
if (node.leftChild == null) return node.rightChild

var nodeCurrent: RBTreeNode<K, V> = node
if (!isRedColor(nodeCurrent.leftChild) && !isRedLeftChild(nodeCurrent.leftChild))
nodeCurrent = moveRedLeft(nodeCurrent)

nodeCurrent.leftChild = notNullNodeAction(
nodeCurrent.leftChild, null
) {child -> removeMinNode(child)}

return balanceTree(nodeCurrent)
protected fun flipColors(node: RBTreeNode<K, V>?): Unit {
if (node == null) return
node.flipColor()
notNullNodeUpdate(node.leftChild) { child -> child.flipColor() }
notNullNodeUpdate(node.rightChild) { child -> child.flipColor() }
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ public abstract class AbstractBSTreeNode<K: Comparable<K>, V, N: AbstractBSTreeN
public val key: K,
public var value: V
): SearchTreeNode<K, V, N> {
public var leftChild: N? = null
public var rightChild: N? = null
public open var leftChild: N? = null
public open var rightChild: N? = null

override fun getChildren(): List<N> {
return listOfNotNull(leftChild, rightChild)
Expand Down
26 changes: 26 additions & 0 deletions lib/src/main/kotlin/tree_tripper/nodes/binary_nodes/RBTreeNode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,23 @@ public class RBTreeNode<K : Comparable<K>, V>(
key: K,
value: V
) : AbstractBSTreeNode<K, V, RBTreeNode<K, V>>(key, value) {
var parent: RBTreeNode<K, V>? = null
var isRed: Boolean = true

override var leftChild: RBTreeNode<K, V>?
get() = super.leftChild
set(value) {
if (value != null) value.parent = this
super.leftChild = value
}

override var rightChild: RBTreeNode<K, V>?
get() = super.rightChild
set(value) {
if (value != null) value.parent = this
super.rightChild = value
}

public constructor(key: K, value: V, isRed: Boolean) : this(key, value) {
this.isRed = isRed
}
Expand All @@ -26,6 +41,17 @@ public class RBTreeNode<K : Comparable<K>, V>(
this.rightChild = rightChild
}

public fun getUncle(): RBTreeNode<K, V>? {
val parent = this.parent
if (parent == null) return null
if (parent.leftChild === this) return parent.rightChild
return parent.leftChild
}

public fun flipColor(): Unit {
isRed = !isRed
}

override fun toStringSimpleView(): String {
return "${super.toStringSimpleView()} - ${colorName()}"
}
Expand Down
55 changes: 0 additions & 55 deletions lib/src/test/kotlin/tree_tripper/binary_trees/RBTreeTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,61 +25,6 @@ class RBTreeTest {
Assertions.assertEquals(0, tree.getSize())
}

@Test
public fun testSimpleInsert() {
tree.insert(0, 0)
tree.assertRoot(RBTreeNode(0, 0, false)) { "Root of RBTree is not equal to inserted node." }
tree.assertIsRBTree()
Assertions.assertEquals(1, tree.getSize())
}

@Test
public fun testValueChangeInsert() {
tree.insert(0, 0)
tree.insert(0, 1)
tree.assertRoot(RBTreeNode(0, 1, false)) { "Root of RBTree is not equal to inserted node." }
}

@ParameterizedTest
@MethodSource("testSortedInsertElementsCases")
public fun testSortedInsert(elements: List<Int>, expectedTreeView: RBTreeNode<Int, Int>) {
val elementsSet = elements.toSet()
insert(elements.sorted())

tree.assertIsRBTree()
Assertions.assertEquals(elementsSet.size, tree.getSize())
tree.assertRoot(expectedTreeView) {
"Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}"
}
}

@ParameterizedTest
@MethodSource("testReverseSortedInsertElementsCases")
public fun testReverseSortedInsert(elements: List<Int>, expectedTreeView: RBTreeNode<Int, Int>) {
val elementsSet = elements.toSet()
insert(elements.sorted().reversed())

tree.assertIsRBTree()
Assertions.assertEquals(elementsSet.size, tree.getSize())
tree.assertRoot(expectedTreeView) {
"Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}"
}
}

@ParameterizedTest
@MethodSource("testUnsortedInsertElementsCases")
public fun testUnsortedInsert(elements: List<Int>, expectedTreeView: RBTreeNode<Int, Int>) {
val elementsSet = elements.toSet()
insert(elements)

tree.assertIsRBTree()
Assertions.assertEquals(elementsSet.size, tree.getSize())
tree.assertRoot(expectedTreeView) {
"Root of RBTree is not equal to inserted node: ${nodeToStringTreeView(expectedTreeView)}\n"
"Tree view: ${tree.toStringWithTreeView()}"
}
}

@ParameterizedTest
@MethodSource("testNodeColorCases")
public fun testNodeColor(expected: Boolean, node: RBTreeNode<Int, Int>?) {
Expand Down

0 comments on commit 5e78d5c

Please sign in to comment.