From 42c89be7856e6ee66c22f91e27c8b12d299397ee Mon Sep 17 00:00:00 2001 From: liuyuxin Date: Wed, 5 Apr 2023 15:44:54 +0800 Subject: [PATCH] feat: add dart code for chapter_tree (#448) --- codes/dart/chapter_tree/avl_tree.dart | 226 ++++++++++++++++++ .../dart/chapter_tree/binary_search_tree.dart | 162 +++++++++++++ codes/dart/chapter_tree/binary_tree.dart | 37 +++ codes/dart/chapter_tree/binary_tree_bfs.dart | 38 +++ codes/dart/chapter_tree/binary_tree_dfs.dart | 62 +++++ codes/dart/utils/tree_node.dart | 19 +- 6 files changed, 533 insertions(+), 11 deletions(-) create mode 100644 codes/dart/chapter_tree/avl_tree.dart create mode 100644 codes/dart/chapter_tree/binary_search_tree.dart create mode 100644 codes/dart/chapter_tree/binary_tree.dart create mode 100644 codes/dart/chapter_tree/binary_tree_bfs.dart create mode 100644 codes/dart/chapter_tree/binary_tree_dfs.dart diff --git a/codes/dart/chapter_tree/avl_tree.dart b/codes/dart/chapter_tree/avl_tree.dart new file mode 100644 index 000000000..5e42b8ecd --- /dev/null +++ b/codes/dart/chapter_tree/avl_tree.dart @@ -0,0 +1,226 @@ +/** + * File: avl_tree.dart + * Created Time: 2023-04-04 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import 'dart:math'; +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +class AVLTree { + TreeNode? root; + + /* 构造方法 */ + AVLTree() { + root = null; + } + + /* 获取结点高度 */ + int height(TreeNode? node) { + return node == null ? -1 : node.height; + } + + /* 更新结点高度 */ + void updateHeight(TreeNode? node) { + // 结点高度等于最高子树高度 + 1 + node!.height = max(height(node.left), height(node.right)) + 1; + } + + /* 获取平衡因子 */ + int balanceFactor(TreeNode? node) { + // 空结点平衡因子为 0 + if (node == null) return 0; + // 结点平衡因子 = 左子树高度 - 右子树高度 + return height(node.left) - height(node.right); + } + + /* 右旋操作 */ + TreeNode? rightRotate(TreeNode? node) { + TreeNode? child = node!.left; + TreeNode? grandChild = child!.right; + // 以 child 为原点,将 node 向右旋转 + child.right = node; + node.left = grandChild; + // 更新结点高度 + updateHeight(node); + updateHeight(child); + // 返回旋转后子树的根结点 + return child; + } + + /* 左旋操作 */ + TreeNode? leftRotate(TreeNode? node) { + TreeNode? child = node!.right; + TreeNode? grandChild = child!.left; + // 以 child 为原点,将 node 向左旋转 + child.left = node; + node.right = grandChild; + // 更新结点高度 + updateHeight(node); + updateHeight(child); + // 返回旋转后子树的根结点 + return child; + } + + /* 执行旋转操作,使该子树重新恢复平衡 */ + TreeNode? rotate(TreeNode? node) { + // 获取结点 node 的平衡因子 + int factor = balanceFactor(node); + // 左偏树 + if (factor > 1) { + if (balanceFactor(node!.left) >= 0) { + // 右旋 + return rightRotate(node); + } else { + // 先左旋后右旋 + node.left = leftRotate(node.left); + return rightRotate(node); + } + } + // 右偏树 + if (factor < -1) { + if (balanceFactor(node!.right) <= 0) { + // 左旋 + return leftRotate(node); + } else { + // 先右旋后左旋 + node.right = rightRotate(node.right); + return leftRotate(node); + } + } + // 平衡树,无需旋转,直接返回 + return node; + } + + /* 插入结点 */ + TreeNode? insert(int val) { + root = insertHelper(root, val); + return root; + } + + /* 递归插入结点(辅助方法) */ + TreeNode? insertHelper(TreeNode? node, int val) { + if (node == null) return TreeNode(val); + /* 1. 查找插入位置,并插入结点 */ + if (val < node.val) + node.left = insertHelper(node.left, val); + else if (val > node.val) + node.right = insertHelper(node.right, val); + else + return node; // 重复结点不插入,直接返回 + updateHeight(node); // 更新结点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node); + // 返回子树的根结点 + return node; + } + + /* 删除结点 */ + TreeNode? remove(int val) { + root = removeHelper(root, val); + return root; + } + + /* 递归删除结点(辅助方法) */ + TreeNode? removeHelper(TreeNode? node, int val) { + if (node == null) return null; + /* 1. 查找结点,并删除之 */ + if (val < node.val) + node.left = removeHelper(node.left, val); + else if (val > node.val) + node.right = removeHelper(node.right, val); + else { + if (node.left == null || node.right == null) { + TreeNode? child = node.left ?? node.right; + // 子结点数量 = 0 ,直接删除 node 并返回 + if (child == null) + return null; + // 子结点数量 = 1 ,直接删除 node + else + node = child; + } else { + // 子结点数量 = 2 ,则将中序遍历的下个结点删除,并用该结点替换当前结点 + TreeNode? temp = getInOrderNext(node.right); + node.right = removeHelper(node.right, temp!.val); + node.val = temp.val; + } + } + updateHeight(node); // 更新结点高度 + /* 2. 执行旋转操作,使该子树重新恢复平衡 */ + node = rotate(node); + // 返回子树的根结点 + return node; + } + + /* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ + TreeNode? getInOrderNext(TreeNode? node) { + if (node == null) return node; + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + while (node!.left != null) { + node = node.left; + } + return node; + } + + /* 查找结点 */ + TreeNode? search(int val) { + TreeNode? cur = root; + // 循环查找,越过叶结点后跳出 + while (cur != null) { + // 目标结点在 cur 的右子树中 + if (val < cur.val) + cur = cur.left; + // 目标结点在 cur 的左子树中 + else if (val > cur.val) + cur = cur.right; + // 目标结点与当前结点相等 + else + break; + } + return cur; + } +} + +void testInsert(AVLTree tree, int val) { + tree.insert(val); + print("\n插入结点 $val 后,AVL 树为"); + printTree(tree.root); +} + +void testRemove(AVLTree tree, int val) { + tree.remove(val); + print("\n删除结点 $val 后,AVL 树为"); + printTree(tree.root); +} + +/* Driver Code */ +void main() { + /* 初始化空 AVL 树 */ + AVLTree 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 的结点 + + /* 查询结点 */ + TreeNode? node = avlTree.search(7); + print("\n查找到的结点对象为 $node,结点值 = ${node!.val}"); +} diff --git a/codes/dart/chapter_tree/binary_search_tree.dart b/codes/dart/chapter_tree/binary_search_tree.dart new file mode 100644 index 000000000..fbebdff3a --- /dev/null +++ b/codes/dart/chapter_tree/binary_search_tree.dart @@ -0,0 +1,162 @@ +/** + * File: binary_search_tree.dart + * Created Time: 2023-04-04 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* 二叉搜索树 */ +TreeNode? root; + +void binarySearchTree(List nums) { + nums.sort(); // 排序数组 + root = buildTree(nums, 0, nums.length - 1); // 构建二叉搜索树 +} + +/* 获取二叉树的根节点 */ +TreeNode? getRoot() { + return root; +} + +/* 构建二叉上搜索树 */ +TreeNode? buildTree(List nums, int i, int j) { + if (i > j) { + return null; + } + // 将数组中间结点作为根结点 + int mid = (i + j) ~/ 2; + TreeNode? root = TreeNode(nums[mid]); + root.left = buildTree(nums, i, mid - 1); + root.right = buildTree(nums, mid + 1, j); + return root; +} + +/* 查找结点 */ +TreeNode? search(int num) { + TreeNode? cur = root; + // 循环查找,越过叶结点后跳出 + while (cur != null) { + // 目标结点在 cur 的右子树中 + if (cur.val < num) + cur = cur.right; + // 目标结点在 cur 的左子树中 + else if (cur.val > num) + cur = cur.left; + // 找到目标结点,跳出循环 + else + break; + } + // 返回目标结点 + return cur; +} + +/* 插入结点 */ +TreeNode? insert(int num) { + // 若树为空,直接提前返回 + if (root == null) return null; + TreeNode? cur = root; + TreeNode? pre = null; + // 循环查找,越过叶结点后跳出 + while (cur != null) { + // 找到重复结点,直接返回 + if (cur.val == num) return null; + pre = cur; + // 插入位置在 cur 的右子树中 + if (cur.val < num) + cur = cur.right; + // 插入位置在 cur 的左子树中 + else + cur = cur.left; + } + // 插入结点 val + TreeNode? node = TreeNode(num); + if (pre!.val < num) + pre.right = node; + else + pre.left = node; + return node; +} + +/* 删除结点 */ +TreeNode? remove(int num) { + // 若树为空,直接提前返回 + if (root == null) return null; + + TreeNode? cur = root; + TreeNode? pre = null; + // 循环查找,越过叶结点后跳出 + while (cur != null) { + // 找到待删除结点,跳出循环 + if (cur.val == num) break; + pre = cur; + // 待删除结点在 cur 的右子树中 + if (cur.val < num) + cur = cur.right; + // 待删除结点在 cur 的左子树中 + else + cur = cur.left; + } + // 若无待删除结点,直接返回 + if (cur == null) return null; + // 子结点数量 = 0 or 1 + if (cur.left == null || cur.right == null) { + // 当子结点数量 = 0 / 1 时, child = null / 该子结点 + TreeNode? child = cur.left ?? cur.right; + // 删除结点 cur + if (pre!.left == cur) + pre.left = child; + else + pre.right = child; + } else { + // 子结点数量 = 2 + // 获取中序遍历中 cur 的下一个结点 + TreeNode? nex = getInOrderNext(cur.right); + int tem = nex!.val; + // 递归删除结点 nex + remove(nex.val); + // 将 nex 的值复制给 cur + cur.val = tem; + } + return cur; +} + +/* 获取中序遍历中的下一个结点(仅适用于 root 有左子结点的情况) */ +TreeNode? getInOrderNext(TreeNode? root) { + if (root == null) return null; + // 循环访问左子结点,直到叶结点时为最小结点,跳出 + while (root!.left != null) { + root = root.left; + } + return root; +} + +/* Driver Code */ +void main() { + /* 初始化二叉搜索树 */ + List nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]; + binarySearchTree(nums); + print("\n初始化的二叉树为\n"); + printTree(getRoot()); + + /* 查找结点 */ + TreeNode? node = search(7); + print("\n查找到的结点对象为 $node,结点值 = ${node?.val}"); + + /* 插入结点 */ + node = insert(16); + print("\n插入节点 16 后,二叉树为\n"); + printTree(getRoot()); + + /* 删除结点 */ + remove(1); + print("\n删除结点 1 后,二叉树为\n"); + printTree(getRoot()); + remove(2); + print("\n删除结点 2 后,二叉树为\n"); + printTree(getRoot()); + remove(4); + print("\n删除结点 4 后,二叉树为\n"); + printTree(getRoot()); +} diff --git a/codes/dart/chapter_tree/binary_tree.dart b/codes/dart/chapter_tree/binary_tree.dart new file mode 100644 index 000000000..c0dba1c24 --- /dev/null +++ b/codes/dart/chapter_tree/binary_tree.dart @@ -0,0 +1,37 @@ +/** + * File: binary_tree.dart + * Created Time: 2023-04-03 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +void main() { + /* 初始化二叉树 */ + // 舒适化结点 + TreeNode n1 = TreeNode(1); + TreeNode n2 = TreeNode(2); + TreeNode n3 = TreeNode(3); + TreeNode n4 = TreeNode(4); + TreeNode n5 = TreeNode(5); + // 构建引用指向(即指针) + n1.left = n2; + n1.right = n3; + n2.left = n4; + n2.right = n5; + print("\n初始化二叉树\n"); + printTree(n1); + + /* 插入与删除结点 */ + TreeNode p = TreeNode(0); + // 在 n1 -> n2 中间插入节点 p + n1.left = p; + p.left = n2; + print("\n插入结点 P 后\n"); + printTree(n1); + // 删除结点 P + n1.left = n2; + print("\n删除结点 P 后\n"); + printTree(n1); +} diff --git a/codes/dart/chapter_tree/binary_tree_bfs.dart b/codes/dart/chapter_tree/binary_tree_bfs.dart new file mode 100644 index 000000000..49462f18c --- /dev/null +++ b/codes/dart/chapter_tree/binary_tree_bfs.dart @@ -0,0 +1,38 @@ +/** + * File: binary_tree_bfs.dart + * Created Time: 2023-04-03 + * Author: liuyuxin (gvenusleo@gmai.com) + */ + +import 'dart:collection'; +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +/* 层序遍历 */ +List levelOrder(TreeNode? root) { + // 初始化队列,加入根节点 + Queue queue = Queue(); + queue.add(root); + // 初始化一个列表,用于保存遍历序列 + List res = []; + while (queue.isNotEmpty) { + TreeNode? node = queue.removeFirst(); // 队列出队 + res.add(node!.val); // 保存结点值 + if (node.left != null) queue.add(node.left); // 左子节点入队 + if (node.right != null) queue.add(node.right); // 右子节点入队 + } + return res; +} + +/* Driver Code */ +void main() { + /* 初始化二叉树 */ + // 这里借助了一个从数组直接生成二叉树的函数 + TreeNode? root = listToTree([1, 2, 3, 4, 5, 6, 7]); + print("\n初始化二叉树\n"); + printTree(root); + + // 层序遍历 + List res = levelOrder(root); + print("\n层序遍历的结点打印序列 = $res"); +} diff --git a/codes/dart/chapter_tree/binary_tree_dfs.dart b/codes/dart/chapter_tree/binary_tree_dfs.dart new file mode 100644 index 000000000..ffd6fa141 --- /dev/null +++ b/codes/dart/chapter_tree/binary_tree_dfs.dart @@ -0,0 +1,62 @@ +/** + * File: binary_tree_dfs.dart + * Created Time: 2023-04-04 + * Author: liuyuxin (gvenusleo@gmail.com) + */ + +import '../utils/print_util.dart'; +import '../utils/tree_node.dart'; + +// 初始化列表,用于存储遍历序列 +List list = []; + +/* 前序遍历 */ +void preOrder(TreeNode? node) { + if (node == null) return; + // 访问优先级:根节点 -> 左子树 -> 右子树 + list.add(node.val); + preOrder(node.left); + preOrder(node.right); +} + +/* 中序遍历 */ +void inOrder(TreeNode? node) { + if (node == null) return; + // 访问优先级:左子树 -> 根节点 -> 右子树 + inOrder(node.left); + list.add(node.val); + inOrder(node.right); +} + +/* 后序遍历 */ +void postOrder(TreeNode? node) { + if (node == null) return; + // 访问优先级:左子树 -> 右子树 -> 根节点 + postOrder(node.left); + postOrder(node.right); + list.add(node.val); +} + +/* Driver Code */ +void main() { + /* 初始化二叉树 */ + // 这里借助了一个从数组直接生成二叉树的函数 + TreeNode? root = listToTree([1, 2, 3, 4, 5, 6, 7]); + print("\n初始化二叉树\n"); + printTree(root); + + /* 前序遍历 */ + list.clear(); + preOrder(root); + print("\n前序遍历的结点打印序列 = $list"); + + /* 中序遍历 */ + list.clear(); + inOrder(root); + print("\n中序遍历的结点打印序列 = $list"); + + /* 后序遍历 */ + list.clear(); + postOrder(root); + print("\n后序遍历的结点打印序列 = $list"); +} diff --git a/codes/dart/utils/tree_node.dart b/codes/dart/utils/tree_node.dart index d7d13b88a..2cba680ab 100644 --- a/codes/dart/utils/tree_node.dart +++ b/codes/dart/utils/tree_node.dart @@ -7,14 +7,12 @@ import 'dart:collection'; class TreeNode { - late int val; // 结点值 - late int height; // 结点高度 - late TreeNode? left; // 左子结点引用 - late TreeNode? right; // 右子结点引用 + int val; // 结点值 + int height; // 结点高度 + TreeNode? left; // 左子结点引用 + TreeNode? right; // 右子结点引用 - TreeNode(int x) { - val = x; - } + TreeNode(this.val, [this.height = 0, this.left, this.right]); } /** @@ -30,14 +28,13 @@ TreeNode? listToTree(List list) { Queue queue = Queue(); queue.add(root); int i = 0; - while (!queue.isEmpty) { - TreeNode? node = queue.first; - queue.removeFirst(); + while (queue.isNotEmpty) { + TreeNode? node = queue.removeFirst(); if (++i >= size) break; node?.left = TreeNode(list[i]); queue.add(node?.left); if (++i >= size) break; - node?.left = TreeNode(list[i]); + node?.right = TreeNode(list[i]); queue.add(node?.right); } return root;