From 5e77a643419e51d5a3cd7689af10b1cde2f8cd8e Mon Sep 17 00:00:00 2001 From: curtishd <131777542+curtishd@users.noreply.github.com> Date: Mon, 25 Mar 2024 16:22:17 +0800 Subject: [PATCH] Add kotlin code for the chapter of tree (#1172) * modified array.kt. * feat(kotlin): add kotlin code for the chapter of stack and queue. * modified array.kt * modified comments. * feat(kotlin): add kotlin code for the chapter of tree. * feat(kotlin): add kotlin code for the chapter of tree. --- .../kotlin/chapter_tree/array_binary_tree.kt | 122 ++++++++++ codes/kotlin/chapter_tree/avl_tree.kt | 208 ++++++++++++++++++ .../kotlin/chapter_tree/binary_search_tree.kt | 138 ++++++++++++ codes/kotlin/chapter_tree/binary_tree.kt | 40 ++++ codes/kotlin/chapter_tree/binary_tree_bfs.kt | 41 ++++ codes/kotlin/chapter_tree/binary_tree_dfs.kt | 64 ++++++ 6 files changed, 613 insertions(+) create mode 100644 codes/kotlin/chapter_tree/array_binary_tree.kt create mode 100644 codes/kotlin/chapter_tree/avl_tree.kt create mode 100644 codes/kotlin/chapter_tree/binary_search_tree.kt create mode 100644 codes/kotlin/chapter_tree/binary_tree.kt create mode 100644 codes/kotlin/chapter_tree/binary_tree_bfs.kt create mode 100644 codes/kotlin/chapter_tree/binary_tree_dfs.kt diff --git a/codes/kotlin/chapter_tree/array_binary_tree.kt b/codes/kotlin/chapter_tree/array_binary_tree.kt new file mode 100644 index 000000000..46c09cef0 --- /dev/null +++ b/codes/kotlin/chapter_tree/array_binary_tree.kt @@ -0,0 +1,122 @@ +/** + * File: array_binary_tree.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree + +/* 数组表示下的二叉树类 */ +class ArrayBinaryTree(private val tree: List) { + /* 列表容量 */ + fun size(): Int { + return tree.size + } + + /* 获取索引为 i 节点的值 */ + fun value(i: Int): Int? { + // 若索引越界,则返回 null ,代表空位 + if (i < 0 || i >= size()) return null + return tree[i] + } + + /* 获取索引为 i 节点的左子节点的索引 */ + fun left(i: Int): Int { + return 2 * i + 1 + } + + /* 获取索引为 i 节点的右子节点的索引 */ + fun right(i: Int): Int { + return 2 * i + 2 + } + + /* 获取索引为 i 节点的父节点的索引 */ + fun parent(i: Int): Int { + return (i - 1) / 2 + } + + /* 层序遍历 */ + fun levelOrder(): List { + val res = ArrayList() + // 直接遍历数组 + for (i in 0..) { + // 若为空位,则返回 + if (value(i) == null) return + // 前序遍历 + if ("pre" == order) res.add(value(i)) + dfs(left(i), order, res) + // 中序遍历 + if ("in" == order) res.add(value(i)) + dfs(right(i), order, res) + // 后序遍历 + if ("post" == order) res.add(value(i)) + } + + /* 前序遍历 */ + fun preOrder(): List { + val res = ArrayList() + dfs(0, "pre", res) + return res + } + + /* 中序遍历 */ + fun inOrder(): List { + val res = ArrayList() + dfs(0, "in", res) + return res + } + + /* 后序遍历 */ + fun postOrder(): List { + val res = ArrayList() + dfs(0, "post", res) + return res + } +} + +/* Driver Code */ +fun main() { + // 初始化二叉树 + // 这里借助了一个从数组直接生成二叉树的函数 + val arr = mutableListOf(1, 2, 3, 4, null, 6, 7, 8, 9, null, null, 12, null, null, 15) + + val root = TreeNode.listToTree(arr) + println("\n初始化二叉树\n") + println("二叉树的数组表示:") + println(arr) + println("二叉树的链表表示:") + printTree(root) + + // 数组表示下的二叉树类 + val abt = ArrayBinaryTree(arr) + + // 访问节点 + val i = 1 + val l = abt.left(i) + val r = abt.right(i) + val p = abt.parent(i) + println("当前节点的索引为 $i ,值为 ${abt.value(i)}") + println("其左子节点的索引为 $l ,值为 ${abt.value(l)}") + println("其右子节点的索引为 $r ,值为 ${abt.value(r)}") + println("其父节点的索引为 $p ,值为 ${abt.value(p)}") + + // 遍历树 + var res = abt.levelOrder() + println("\n层序遍历为:$res") + res = abt.preOrder() + println("前序遍历为:$res") + res = abt.inOrder() + println("中序遍历为:$res") + res = abt.postOrder() + println("后序遍历为:$res") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_tree/avl_tree.kt b/codes/kotlin/chapter_tree/avl_tree.kt new file mode 100644 index 000000000..8f2d18b24 --- /dev/null +++ b/codes/kotlin/chapter_tree/avl_tree.kt @@ -0,0 +1,208 @@ +/** + * File: avl_tree.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree +import kotlin.math.max + +/* AVL 树 */ +class AVLTree { + var root: TreeNode? = null // 根节点 + + /* 获取节点高度 */ + fun height(node: TreeNode?): Int { + // 空节点高度为 -1 ,叶节点高度为 0 + return node?.height ?: -1 + } + + /* 更新节点高度 */ + private fun updateHeight(node: TreeNode?) { + // 节点高度等于最高子树高度 + 1 + node?.height = (max(height(node?.left).toDouble(), height(node?.right).toDouble()) + 1).toInt() + } + + /* 获取平衡因子 */ + fun balanceFactor(node: TreeNode?): Int { + // 空节点平衡因子为 0 + if (node == null) return 0 + // 节点平衡因子 = 左子树高度 - 右子树高度 + return height(node.left) - height(node.right) + } + + /* 右旋操作 */ + private fun rightRotate(node: TreeNode?): TreeNode { + val child = node!!.left + val grandChild = child!!.right + // 以 child 为原点,将 node 向右旋转 + child.right = node + node.left = grandChild + // 更新节点高度 + updateHeight(node) + updateHeight(child) + // 返回旋转后子树的根节点 + return child + } + + /* 左旋操作 */ + private fun leftRotate(node: TreeNode?): TreeNode { + val child = node!!.right + val grandChild = child!!.left + // 以 child 为原点,将 node 向左旋转 + child.left = node + node.right = grandChild + // 更新节点高度 + updateHeight(node) + updateHeight(child) + // 返回旋转后子树的根节点 + return child + } + + /* 执行旋转操作,使该子树重新恢复平衡 */ + private fun rotate(node: TreeNode): TreeNode { + // 获取节点 node 的平衡因子 + val balanceFactor = balanceFactor(node) + // 左偏树 + if (balanceFactor > 1) { + if (balanceFactor(node.left) >= 0) { + // 右旋 + return rightRotate(node) + } else { + // 先左旋后右旋 + node.left = leftRotate(node.left) + return rightRotate(node) + } + } + // 右偏树 + if (balanceFactor < -1) { + if (balanceFactor(node.right) <= 0) { + // 左旋 + return leftRotate(node) + } else { + // 先右旋后左旋 + node.right = rightRotate(node.right) + return leftRotate(node) + } + } + // 平衡树,无须旋转,直接返回 + return node + } + + /* 插入节点 */ + fun insert(value: Int) { + root = insertHelper(root, value) + } + + /* 递归插入节点(辅助方法) */ + private fun insertHelper(n: TreeNode?, value: Int): TreeNode { + if (n == null) + return TreeNode(value) + var node = n + /* 1. 查找插入位置并插入节点 */ + if (value < node.value) node.left = insertHelper(node.left, value) + else if (value > node.value) node.right = insertHelper(node.right, value) + else return node // 重复节点不插入,直接返回 + + updateHeight(node) // 更新节点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node) + // 返回子树的根节点 + return node + } + + /* 删除节点 */ + fun remove(value: Int) { + root = removeHelper(root, value) + } + + /* 递归删除节点(辅助方法) */ + private fun removeHelper(n: TreeNode?, value: Int): TreeNode? { + var node = n ?: return null + /* 1. 查找节点并删除 */ + if (value < node.value) node.left = removeHelper(node.left, value) + else if (value > node.value) node.right = removeHelper(node.right, value) + else { + if (node.left == null || node.right == null) { + val child = if (node.left != null) node.left else node.right + // 子节点数量 = 0 ,直接删除 node 并返回 + if (child == null) return null + else node = child + } else { + // 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 + var temp = node.right + while (temp!!.left != null) { + temp = temp.left + } + node.right = removeHelper(node.right, temp.value) + node.value = temp.value + } + } + updateHeight(node) // 更新节点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node) + // 返回子树的根节点 + return node + } + + /* 查找节点 */ + fun search(value: Int): TreeNode? { + var cur = root + // 循环查找,越过叶节点后跳出 + while (cur != null) { + // 目标节点在 cur 的右子树中 + cur = if (cur.value < value) cur.right!! + else (if (cur.value > value) cur.left + else break)!! + } + // 返回目标节点 + return cur + } +} + +fun testInsert(tree: AVLTree, value: Int) { + tree.insert(value) + println("\n插入节点 $value 后,AVL 树为") + printTree(tree.root) +} + +fun testRemove(tree: AVLTree, value: Int) { + tree.remove(value) + println("\n删除节点 $value 后,AVL 树为") + printTree(tree.root) +} + +/* Driver Code */ +fun main() { + /* 初始化空 AVL 树 */ + val avlTree = AVLTree() + + /* 插入节点 */ + // 请关注插入节点后,AVL 树是如何保持平衡的 + testInsert(avlTree, 1) + testInsert(avlTree, 2) + testInsert(avlTree, 3) + testInsert(avlTree, 4) + testInsert(avlTree, 5) + testInsert(avlTree, 8) + testInsert(avlTree, 7) + testInsert(avlTree, 9) + testInsert(avlTree, 10) + testInsert(avlTree, 6) + + /* 插入重复节点 */ + testInsert(avlTree, 7) + + /* 删除节点 */ + // 请关注删除节点后,AVL 树是如何保持平衡的 + testRemove(avlTree, 8) // 删除度为 0 的节点 + testRemove(avlTree, 5) // 删除度为 1 的节点 + testRemove(avlTree, 4) // 删除度为 2 的节点 + + /* 查询节点 */ + val node = avlTree.search(7) + println("\n 查找到的节点对象为 $node,节点值 = ${node?.value}") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_tree/binary_search_tree.kt b/codes/kotlin/chapter_tree/binary_search_tree.kt new file mode 100644 index 000000000..0d4513e0c --- /dev/null +++ b/codes/kotlin/chapter_tree/binary_search_tree.kt @@ -0,0 +1,138 @@ +/** + * File: binary_search_tree.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree + +/* 二叉搜索树 */ +class BinarySearchTree { + private var root: TreeNode? = null + + /* 获取二叉树根节点 */ + fun getRoot(): TreeNode? { + return root + } + + /* 查找节点 */ + fun search(num: Int): TreeNode? { + var cur = root + // 循环查找,越过叶节点后跳出 + while (cur != null) { + // 目标节点在 cur 的右子树中 + cur = if (cur.value < num) cur.right + // 目标节点在 cur 的左子树中 + else if (cur.value > num) cur.left + // 找到目标节点,跳出循环 + else break + } + // 返回目标节点 + return cur + } + + /* 插入节点 */ + fun insert(num: Int) { + // 若树为空,则初始化根节点 + if (root == null) { + root = TreeNode(num) + return + } + var cur = root + var pre: TreeNode? = null + // 循环查找,越过叶节点后跳出 + while (cur != null) { + // 找到重复节点,直接返回 + if (cur.value == num) return + pre = cur + // 插入位置在 cur 的右子树中 + cur = if (cur.value < num) cur.right + // 插入位置在 cur 的左子树中 + else cur.left + } + // 插入节点 + val node = TreeNode(num) + if (pre?.value!! < num) pre.right = node + else pre.left = node + } + + /* 删除节点 */ + fun remove(num: Int) { + // 若树为空,直接提前返回 + if (root == null) return + var cur = root + var pre: TreeNode? = null + // 循环查找,越过叶节点后跳出 + while (cur != null) { + // 找到待删除节点,跳出循环 + if (cur.value == num) break + pre = cur + // 待删除节点在 cur 的右子树中 + cur = if (cur.value < num) cur.right + // 待删除节点在 cur 的左子树中 + else cur.left + } + // 若无待删除节点,则直接返回 + if (cur == null) return + // 子节点数量 = 0 or 1 + if (cur.left == null || cur.right == null) { + // 当子节点数量 = 0 / 1 时, child = null / 该子节点 + val child = if (cur.left != null) cur.left else cur.right + // 删除节点 cur + if (cur != root) { + if (pre!!.left == cur) pre.left = child + else pre.right = child + } else { + // 若删除节点为根节点,则重新指定根节点 + root = child + } + // 子节点数量 = 2 + } else { + // 获取中序遍历中 cur 的下一个节点 + var tmp = cur.right + while (tmp!!.left != null) { + tmp = tmp.left + } + // 递归删除节点 tmp + remove(tmp.value) + // 用 tmp 覆盖 cur + cur.value = tmp.value + } + } +} + +/* Driver Code */ +fun main() { + /* 初始化二叉搜索树 */ + val bst = BinarySearchTree() + // 请注意,不同的插入顺序会生成不同的二叉树,该序列可以生成一个完美二叉树 + val nums = intArrayOf(8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15) + for (num in nums) { + bst.insert(num) + } + println("\n初始化的二叉树为\n") + printTree(bst.getRoot()) + + /* 查找节点 */ + val node = bst.search(7) + println("查找到的节点对象为 $node,节点值 = ${node?.value}") + + /* 插入节点 */ + bst.insert(16) + println("\n插入节点 16 后,二叉树为\n") + printTree(bst.getRoot()) + + /* 删除节点 */ + bst.remove(1) + println("\n删除节点 1 后,二叉树为\n") + printTree(bst.getRoot()) + bst.remove(2) + println("\n删除节点 2 后,二叉树为\n") + printTree(bst.getRoot()) + bst.remove(4) + println("\n删除节点 4 后,二叉树为\n") + printTree(bst.getRoot()) +} \ No newline at end of file diff --git a/codes/kotlin/chapter_tree/binary_tree.kt b/codes/kotlin/chapter_tree/binary_tree.kt new file mode 100644 index 000000000..aca6742ca --- /dev/null +++ b/codes/kotlin/chapter_tree/binary_tree.kt @@ -0,0 +1,40 @@ +/** + * File: binary_tree.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree + +/* Driver Code */ +fun main() { + /* 初始化二叉树 */ + // 初始化节点 + val n1 = TreeNode(1) + val n2 = TreeNode(2) + val n3 = TreeNode(3) + val n4 = TreeNode(4) + val n5 = TreeNode(5) + // 构建节点之间的引用(指针) + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 + println("\n初始化二叉树\n") + printTree(n1) + + /* 插入与删除节点 */ + val P = TreeNode(0) + // 在 n1 -> n2 中间插入节点 P + n1.left = P + P.left = n2 + println("\n插入节点 P 后\n") + printTree(n1) + // 删除节点 P + n1.left = n2 + println("\n删除节点 P 后\n") + printTree(n1) +} \ No newline at end of file diff --git a/codes/kotlin/chapter_tree/binary_tree_bfs.kt b/codes/kotlin/chapter_tree/binary_tree_bfs.kt new file mode 100644 index 000000000..3bd6b850d --- /dev/null +++ b/codes/kotlin/chapter_tree/binary_tree_bfs.kt @@ -0,0 +1,41 @@ +/** + * File: binary_tree_bfs.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree +import java.util.* + +/* 层序遍历 */ +fun levelOrder(root: TreeNode?): MutableList { + // 初始化队列,加入根节点 + val queue = LinkedList() + queue.add(root) + // 初始化一个列表,用于保存遍历序列 + val list = ArrayList() + while (!queue.isEmpty()) { + val node = queue.poll() // 队列出队 + list.add(node?.value!!) // 保存节点值 + if (node.left != null) queue.offer(node.left) // 左子节点入队 + + if (node.right != null) queue.offer(node.right) // 右子节点入队 + } + return list +} + +/* Driver Code */ +fun main() { + /* 初始化二叉树 */ + // 这里借助了一个从数组直接生成二叉树的函数 + val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7)) + println("\n初始化二叉树\n") + printTree(root) + + /* 层序遍历 */ + val list = levelOrder(root) + println("\n层序遍历的节点打印序列 = $list") +} \ No newline at end of file diff --git a/codes/kotlin/chapter_tree/binary_tree_dfs.kt b/codes/kotlin/chapter_tree/binary_tree_dfs.kt new file mode 100644 index 000000000..cefae4e27 --- /dev/null +++ b/codes/kotlin/chapter_tree/binary_tree_dfs.kt @@ -0,0 +1,64 @@ +/** + * File: binary_tree_dfs.kt + * Created Time: 2024-01-25 + * Author: curtishd (1023632660@qq.com) + */ + +package chapter_tree + +import utils.TreeNode +import utils.printTree + +// 初始化列表,用于存储遍历序列 +var list = ArrayList() + +/* 前序遍历 */ +fun preOrder(root: TreeNode?) { + if (root == null) return + // 访问优先级:根节点 -> 左子树 -> 右子树 + list.add(root.value) + preOrder(root.left) + preOrder(root.right) +} + +/* 中序遍历 */ +fun inOrder(root: TreeNode?) { + if (root == null) return + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(root.left) + list.add(root.value) + inOrder(root.right) +} + +/* 后序遍历 */ +fun postOrder(root: TreeNode?) { + if (root == null) return + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(root.left) + postOrder(root.right) + list.add(root.value) +} + +/* Driver Code */ +fun main() { + /* 初始化二叉树 */ + // 这里借助了一个从数组直接生成二叉树的函数 + val root = TreeNode.listToTree(mutableListOf(1, 2, 3, 4, 5, 6, 7)) + println("\n初始化二叉树\n") + printTree(root) + + /* 前序遍历 */ + list.clear() + preOrder(root) + println("\n前序遍历的节点打印序列 = $list") + + /* 中序遍历 */ + list.clear() + inOrder(root) + println("\n中序遍历的节点打印序列 = $list") + + /* 后序遍历 */ + list.clear() + postOrder(root) + println("\n后序遍历的节点打印序列 = $list") +} \ No newline at end of file