-
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add tree node and binary tree learning files
- Loading branch information
1 parent
80a289d
commit b273462
Showing
4 changed files
with
1,006 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,339 @@ | ||
{ | ||
"cells": [ | ||
{ | ||
"cell_type": "markdown", | ||
"id": "a961f6a6", | ||
"metadata": {}, | ||
"source": [ | ||
"# Learn Binary Tree\n", | ||
"## Resource from:\n", | ||
"### https://www.baeldung.com/kotlin/binary-tree\n", | ||
"### https://stackoverflow.com/questions/4965335/how-to-print-binary-tree-diagram-in-java" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 11, | ||
"id": "cd60f598", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"/**\n", | ||
" * An ADT for a binary search tree.\n", | ||
" * Note that this data type is neither immutable nor thread safe.\n", | ||
" */\n", | ||
"class Node(\n", | ||
" var key: Int,\n", | ||
" var left: Node? = null,\n", | ||
" var right: Node? = null\n", | ||
") {\n", | ||
" /**\n", | ||
" * Return a node with given value. If no such node exists, return null.\n", | ||
" * @param value\n", | ||
" */\n", | ||
" fun find(value: Int): Node? = when {\n", | ||
" this.key > value -> left?.find(value)\n", | ||
" this.key < value -> right?.find(value)\n", | ||
" else -> this\n", | ||
" }\n", | ||
"\n", | ||
" /**\n", | ||
" * Insert a given value into the tree.\n", | ||
" * After insertion, the tree should contain a node with the given value.\n", | ||
" * If the tree already contains the given value, nothing is performed.\n", | ||
" * @param value\n", | ||
" */\n", | ||
" fun insert(value: Int) {\n", | ||
" if (value > this.key) {\n", | ||
" if (this.right == null) {\n", | ||
" this.right = Node(value)\n", | ||
" } else {\n", | ||
" this.right?.insert(value)\n", | ||
" }\n", | ||
" } else if (value < this.key) {\n", | ||
" if (this.left == null) {\n", | ||
" this.left = Node(value)\n", | ||
" } else {\n", | ||
" this.left?.insert(value)\n", | ||
" }\n", | ||
" }\n", | ||
" }\n", | ||
"\n", | ||
" /**\n", | ||
" * Delete the value from the given tree. If the tree does not contain the value, the tree remains unchanged.\n", | ||
" * @param value\n", | ||
" */\n", | ||
" fun delete(value: Int) {\n", | ||
" when {\n", | ||
" value > key -> scan(value, this.right, this)\n", | ||
" value < key -> scan(value, this.left, this)\n", | ||
" else -> removeNode(this, null)\n", | ||
" }\n", | ||
" }\n", | ||
"\n", | ||
" /**\n", | ||
" * Scan the tree in the search of the given value.\n", | ||
" * @param value\n", | ||
" * @param node sub-tree that potentially might contain the sought value\n", | ||
" * @param parent node's parent\n", | ||
" */\n", | ||
" private fun scan(value: Int, node: Node?, parent: Node?) {\n", | ||
" if (node == null) {\n", | ||
" System.out.println(\"value \" + value\n", | ||
" + \" seems not present in the tree.\")\n", | ||
" return\n", | ||
" }\n", | ||
" when {\n", | ||
" value > node.key -> scan(value, node.right, node)\n", | ||
" value < node.key -> scan(value, node.left, node)\n", | ||
" value == node.key -> removeNode(node, parent)\n", | ||
" }\n", | ||
"\n", | ||
" }\n", | ||
"\n", | ||
" /**\n", | ||
" * Remove the node.\n", | ||
" *\n", | ||
" * Removal process depends on how many children the node has.\n", | ||
" *\n", | ||
" * @param node node that is to be removed\n", | ||
" * @param parent parent of the node to be removed\n", | ||
" */\n", | ||
" private fun removeNode(node: Node, parent: Node?) {\n", | ||
" node.left?.let { leftChild ->\n", | ||
" run {\n", | ||
" node.right?.let {\n", | ||
" removeTwoChildNode(node)\n", | ||
" } ?: removeSingleChildNode(node, leftChild)\n", | ||
" }\n", | ||
" } ?: run {\n", | ||
" node.right?.let { rightChild -> removeSingleChildNode(node, rightChild) } ?: removeNoChildNode(node, parent)\n", | ||
" }\n", | ||
"\n", | ||
"\n", | ||
" }\n", | ||
"\n", | ||
" /**\n", | ||
" * Remove the node without children.\n", | ||
" * @param node\n", | ||
" * @param parent\n", | ||
" */\n", | ||
" private fun removeNoChildNode(node: Node, parent: Node?) {\n", | ||
" parent?.let { p ->\n", | ||
" if (node == p.left) {\n", | ||
" p.left = null\n", | ||
" } else if (node == p.right) {\n", | ||
" p.right = null\n", | ||
" }\n", | ||
" } ?: throw IllegalStateException(\n", | ||
" \"Can not remove the root node without child nodes\")\n", | ||
"\n", | ||
" }\n", | ||
"\n", | ||
" /**\n", | ||
" * Remove a node that has two children.\n", | ||
" *\n", | ||
" * The process of elimination is to find the biggest key in the left sub-tree and replace the key of the\n", | ||
" * node that is to be deleted with that key.\n", | ||
" */\n", | ||
" private fun removeTwoChildNode(node: Node) {\n", | ||
" val leftChild = node.left!!\n", | ||
" leftChild.right?.let {\n", | ||
" val maxParent = findParentOfMaxChild(leftChild)\n", | ||
" maxParent.right?.let {\n", | ||
" node.key = it.key\n", | ||
" maxParent.right = null\n", | ||
" } ?: throw IllegalStateException(\"Node with max child must have the right child!\")\n", | ||
"\n", | ||
" } ?: run {\n", | ||
" node.key = leftChild.key\n", | ||
" node.left = leftChild.left\n", | ||
" }\n", | ||
"\n", | ||
" }\n", | ||
"\n", | ||
" /**\n", | ||
" * Return a node whose right child contains the biggest value in the given sub-tree.\n", | ||
" * Assume that the node n has a non-null right child.\n", | ||
" *\n", | ||
" * @param n\n", | ||
" */\n", | ||
" private fun findParentOfMaxChild(n: Node): Node {\n", | ||
" return n.right?.let { r -> r.right?.let { findParentOfMaxChild(r) } ?: n }\n", | ||
" ?: throw IllegalArgumentException(\"Right child must be non-null\")\n", | ||
"\n", | ||
" }\n", | ||
"\n", | ||
" /**\n", | ||
" * Remove a parent that has only one child.\n", | ||
" * Removal is effectively is just coping the data from the child parent to the parent parent.\n", | ||
" * @param parent Node to be deleted. Assume that it has just one child\n", | ||
" * @param child Assume it is a child of the parent\n", | ||
" */\n", | ||
" private fun removeSingleChildNode(parent: Node, child: Node) {\n", | ||
" parent.key = child.key\n", | ||
" parent.left = child.left\n", | ||
" parent.right = child.right\n", | ||
" }\n", | ||
"\n", | ||
" fun visit(): Array<Int> {\n", | ||
" val a = left?.visit() ?: emptyArray()\n", | ||
" val b = right?.visit() ?: emptyArray()\n", | ||
" return a + arrayOf(key) + b\n", | ||
" }\n", | ||
"}" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 12, | ||
"id": "55499b3a", | ||
"metadata": {}, | ||
"outputs": [], | ||
"source": [ | ||
"class BTreePrinter {\n", | ||
" fun printNode(root: Node?) {\n", | ||
" val maxLevel = maxLevel(root)\n", | ||
" printNodeInternal(listOf(root), 1, maxLevel)\n", | ||
" }\n", | ||
"\n", | ||
" private fun printNodeInternal(\n", | ||
" nodes: List<Node?>,\n", | ||
" level: Int,\n", | ||
" maxLevel: Int\n", | ||
" ) {\n", | ||
" if (nodes.isEmpty() || isAllElementsNull(nodes)) return\n", | ||
" val floor = maxLevel - level\n", | ||
" val edgeLines = 2.0.pow((floor - 1).coerceAtLeast(0).toDouble()).toInt()\n", | ||
" val firstSpaces = 2.0.pow(floor.toDouble()).toInt() - 1\n", | ||
" val betweenSpaces = 2.0.pow((floor + 1).toDouble()).toInt() - 1\n", | ||
" printWhitespaces(firstSpaces)\n", | ||
" val newNodes: MutableList<Node?> = ArrayList()\n", | ||
" for (node in nodes) {\n", | ||
" if (node != null) {\n", | ||
" print(node.key)\n", | ||
" newNodes.add(node.left)\n", | ||
" newNodes.add(node.right)\n", | ||
" } else {\n", | ||
" newNodes.add(null)\n", | ||
" newNodes.add(null)\n", | ||
" print(\" \")\n", | ||
" }\n", | ||
" printWhitespaces(betweenSpaces)\n", | ||
" }\n", | ||
" println(\"\")\n", | ||
" for (i in 1..edgeLines) {\n", | ||
" for (j in nodes.indices) {\n", | ||
" printWhitespaces(firstSpaces - i)\n", | ||
" if (nodes[j] == null) {\n", | ||
" printWhitespaces(edgeLines + edgeLines + i + 1)\n", | ||
" continue\n", | ||
" }\n", | ||
" if (nodes[j]!!.left != null) print(\"/\") else printWhitespaces(1)\n", | ||
" printWhitespaces(i + i - 1)\n", | ||
" if (nodes[j]!!.right != null) print(\"\\\\\") else printWhitespaces(1)\n", | ||
" printWhitespaces(edgeLines + edgeLines - i)\n", | ||
" }\n", | ||
" println(\"\")\n", | ||
" }\n", | ||
" printNodeInternal(newNodes, level + 1, maxLevel)\n", | ||
" }\n", | ||
"\n", | ||
" private fun printWhitespaces(count: Int) {\n", | ||
" for (i in 0 until count) print(\" \")\n", | ||
" }\n", | ||
"\n", | ||
" private fun maxLevel(node: Node?): Int {\n", | ||
" return if (node == null) 0 else Math.max(maxLevel(node.left), maxLevel(node.right)) + 1\n", | ||
" }\n", | ||
"\n", | ||
" private fun <T> isAllElementsNull(list: List<T?>): Boolean {\n", | ||
" for (`object` in list) {\n", | ||
" if (`object` != null) return false\n", | ||
" }\n", | ||
" return true\n", | ||
" }\n", | ||
"}" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 38, | ||
"id": "109cd95e", | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
"Node with value 4 [left = 1, right = 5]\n", | ||
"Delete node with key = 3\n", | ||
"Tree content after the node elimination: 1, 2, 3, 4, 5, 6, 7\n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"val tree = Node(4)\n", | ||
"val keys = arrayOf(1, 2, 3, 4, 5, 6, 7)\n", | ||
"\n", | ||
"for (key in keys) {\n", | ||
" tree.insert(key)\n", | ||
"}\n", | ||
"\n", | ||
"val node = tree.find(4)!!\n", | ||
"println(\"Node with value ${node.key} [left = ${node.left?.key}, right = ${node.right?.key}]\")\n", | ||
"println(\"Delete node with key = 3\")\n", | ||
"//node.delete(3)\n", | ||
"print(\"Tree content after the node elimination: \")\n", | ||
"println(tree.visit().joinToString { it.toString() })" | ||
] | ||
}, | ||
{ | ||
"cell_type": "code", | ||
"execution_count": 39, | ||
"id": "3d25e7a8", | ||
"metadata": {}, | ||
"outputs": [ | ||
{ | ||
"name": "stdout", | ||
"output_type": "stream", | ||
"text": [ | ||
" 4 \n", | ||
" / \\ \n", | ||
" / \\ \n", | ||
" / \\ \n", | ||
" / \\ \n", | ||
" 1 5 \n", | ||
" \\ \\ \n", | ||
" \\ \\ \n", | ||
" 2 6 \n", | ||
" \\ \\ \n", | ||
" 3 7 \n", | ||
" \n" | ||
] | ||
} | ||
], | ||
"source": [ | ||
"BTreePrinter().printNode(tree)" | ||
] | ||
} | ||
], | ||
"metadata": { | ||
"kernelspec": { | ||
"display_name": "Kotlin", | ||
"language": "kotlin", | ||
"name": "kotlin" | ||
}, | ||
"language_info": { | ||
"codemirror_mode": "text/x-kotlin", | ||
"file_extension": ".kt", | ||
"mimetype": "text/x-kotlin", | ||
"name": "kotlin", | ||
"nbconvert_exporter": "", | ||
"pygments_lexer": "kotlin", | ||
"version": "1.6.20-dev-6372" | ||
} | ||
}, | ||
"nbformat": 4, | ||
"nbformat_minor": 5 | ||
} |
Oops, something went wrong.