diff --git a/codes/ruby/chapter_tree/array_binary_tree.rb b/codes/ruby/chapter_tree/array_binary_tree.rb new file mode 100644 index 000000000..0492e3106 --- /dev/null +++ b/codes/ruby/chapter_tree/array_binary_tree.rb @@ -0,0 +1,124 @@ +=begin +File: array_binary_tree.rb +Created Time: 2024-04-17 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 数组表示下的二叉树类 ### +class ArrayBinaryTree + ### 构造方法 ### + def initialize(arr) + @tree = arr.to_a + end + + ### 列表容量 ### + def size + @tree.length + end + + ### 获取索引为 i 节点的值 ### + def val(i) + # 若索引越界,则返回 nil ,代表空位 + return if i < 0 || i >= size + + @tree[i] + end + + ### 获取索引为 i 节点的左子节点的索引 ### + def left(i) + 2 * i + 1 + end + + ### 获取索引为 i 节点的右子节点的索引 ### + def right(i) + 2 * i + 2 + end + + ### 获取索引为 i 节点的父节点的索引 ### + def parent(i) + (i - 1) / 2 + end + + ### 层序遍历 ### + def level_order + @res = [] + + # 直接遍历数组 + for i in 0...size + @res << val(i) unless val(i).nil? + end + + @res + end + + ### 深度优先遍历 ### + def dfs(i, order) + return if val(i).nil? + # 前序遍历 + @res << val(i) if order == :pre + dfs(left(i), order) + # 中序遍历 + @res << val(i) if order == :in + dfs(right(i), order) + # 后序遍历 + @res << val(i) if order == :post + end + + ### 前序遍历 ### + def pre_order + @res = [] + dfs(0, :pre) + @res + end + + ### 中序遍历 ### + def in_order + @res = [] + dfs(0, :in) + @res + end + + ### 后序遍历 ### + def post_order + @res = [] + dfs(0, :post) + @res + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化二叉树 + # 这里借助了一个从数组直接生成二叉树的函数 + arr = [1, 2, 3, 4, nil, 6, 7, 8, 9, nil, nil, 12, nil, nil, 15] + root = arr_to_tree(arr) + puts "\n初始化二叉树\n\n" + puts '二叉树的数组表示:' + pp arr + puts '二叉树的链表表示:' + print_tree(root) + + # 数组表示下的二叉树类 + abt = ArrayBinaryTree.new(arr) + + # 访问节点 + i = 1 + l, r, _p = abt.left(i), abt.right(i), abt.parent(i) + puts "\n当前节点的索引为 #{i} ,值为 #{abt.val(i).inspect}" + puts "其左子节点的索引为 #{l} ,值为 #{abt.val(l).inspect}" + puts "其右子节点的索引为 #{r} ,值为 #{abt.val(r).inspect}" + puts "其父节点的索引为 #{_p} ,值为 #{abt.val(_p).inspect}" + + # 遍历树 + res = abt.level_order + puts "\n层序遍历为: #{res}" + res = abt.pre_order + puts "前序遍历为: #{res}" + res = abt.in_order + puts "中序遍历为: #{res}" + res = abt.post_order + puts "后序遍历为: #{res}" +end diff --git a/codes/ruby/chapter_tree/avl_tree.rb b/codes/ruby/chapter_tree/avl_tree.rb new file mode 100644 index 000000000..2e4db37d7 --- /dev/null +++ b/codes/ruby/chapter_tree/avl_tree.rb @@ -0,0 +1,216 @@ +=begin +File: avl_tree.rb +Created Time: 2024-04-17 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### AVL 树 ### +class AVLTree + ### 构造方法 ### + def initialize + @root = nil + end + + ### 获取二叉树根节点 ### + def get_root + @root + end + + ### 获取节点高度 ### + def height(node) + # 空节点高度为 -1 ,叶节点高度为 0 + return node.height unless node.nil? + + -1 + end + + ### 更新节点高度 ### + def update_height(node) + # 节点高度等于最高子树高度 + 1 + node.height = [height(node.left), height(node.right)].max + 1 + end + + ### 获取平衡因子 ### + def balance_factor(node) + # 空节点平衡因子为 0 + return 0 if node.nil? + + # 节点平衡因子 = 左子树高度 - 右子树高度 + height(node.left) - height(node.right) + end + + ### 右旋操作 ### + def right_rotate(node) + child = node.left + grand_child = child.right + # 以 child 为原点,将 node 向右旋转 + child.right = node + node.left = grand_child + # 更新节点高度 + update_height(node) + update_height(child) + # 返回旋转后子树的根节点 + child + end + + ### 左旋操作 ### + def left_rotate(node) + child = node.right + grand_child = child.left + # 以 child 为原点,将 node 向左旋转 + child.left = node + node.right = grand_child + # 更新节点高度 + update_height(node) + update_height(child) + # 返回旋转后子树的根节点 + child + end + + ### 执行旋转操作,使该子树重新恢复平衡 ### + def rotate(node) + # 获取节点 node 的平衡因子 + balance_factor = balance_factor(node) + # 左遍树 + if balance_factor > 1 + if balance_factor(node.left) >= 0 + # 右旋 + return right_rotate(node) + else + # 先左旋后右旋 + node.left = left_rotate(node.left) + return right_rotate(node) + end + # 右遍树 + elsif balance_factor < -1 + if balance_factor(node.right) <= 0 + # 左旋 + return left_rotate(node) + else + # 先右旋后左旋 + node.right = right_rotate(node.right) + return left_rotate(node) + end + end + # 平衡树,无须旋转,直接返回 + node + end + + ### 插入节点 ### + def insert(val) + @root = insert_helper(@root, val) + end + + ### 递归插入节点(辅助方法)### + def insert_helper(node, val) + return TreeNode.new(val) if node.nil? + # 1. 查找插入位置并插入节点 + if val < node.val + node.left = insert_helper(node.left, val) + elsif val > node.val + node.right = insert_helper(node.right, val) + else + # 重复节点不插入,直接返回 + return node + end + # 更新节点高度 + update_height(node) + # 2. 执行旋转操作,使该子树重新恢复平衡 + rotate(node) + end + + ### 删除节点 ### + def remove(val) + @root = remove_helper(@root, val) + end + + ### 递归删除节点(辅助方法)### + def remove_helper(node, val) + return if node.nil? + # 1. 查找节点并删除 + if val < node.val + node.left = remove_helper(node.left, val) + elsif val > node.val + node.right = remove_helper(node.right, val) + else + if node.left.nil? || node.right.nil? + child = node.left || node.right + # 子节点数量 = 0 ,直接删除 node 并返回 + return if child.nil? + # 子节点数量 = 1 ,直接删除 node + node = child + else + # 子节点数量 = 2 ,则将中序遍历的下个节点删除,并用该节点替换当前节点 + temp = node.right + while !temp.left.nil? + temp = temp.left + end + node.right = remove_helper(node.right, temp.val) + node.val = temp.val + end + end + # 更新节点高度 + update_height(node) + # 2. 执行旋转操作,使该子树重新恢复平衡 + rotate(node) + end + + ### 查找节点 ### + def search(val) + cur = @root + # 循环查找,越过叶节点后跳出 + while !cur.nil? + # 目标节点在 cur 的右子树中 + if cur.val < val + cur = cur.right + # 目标节点在 cur 的左子树中 + elsif cur.val > val + cur = cur.left + # 找到目标节点,跳出循环 + else + break + end + end + # 返回目标节点 + cur + end +end + +### Driver Code ### +if __FILE__ == $0 + def test_insert(tree, val) + tree.insert(val) + puts "\n插入节点 #{val} 后,AVL 树为" + print_tree(tree.get_root) + end + + def test_remove(tree, val) + tree.remove(val) + puts "\n删除节点 #{val} 后,AVL 树为" + print_tree(tree.get_root) + end + + # 初始化空 AVL 树 + avl_tree = AVLTree.new + + # 插入节点 + # 请关注插入节点后,AVL 树是如何保持平衡的 + for val in [1, 2, 3, 4, 5, 8, 7, 9, 10, 6] + test_insert(avl_tree, val) + end + + # 插入重复节点 + test_insert(avl_tree, 7) + + # 删除节点 + # 请关注删除节点后,AVL 树是如何保持平衡的 + test_remove(avl_tree, 8) # 删除度为 0 的节点 + test_remove(avl_tree, 5) # 删除度为 1 的节点 + test_remove(avl_tree, 4) # 删除度为 2 的节点 + + result_node = avl_tree.search(7) + puts "\n查找到的节点对象为 #{result_node},节点值 = #{result_node.val}" +end diff --git a/codes/ruby/chapter_tree/binary_search_tree.rb b/codes/ruby/chapter_tree/binary_search_tree.rb new file mode 100644 index 000000000..7b012797b --- /dev/null +++ b/codes/ruby/chapter_tree/binary_search_tree.rb @@ -0,0 +1,161 @@ +=begin +File: binary_search_tree.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 二叉搜索树 ### +class BinarySearchTree + ### 构造方法 ### + def initialize + # 初始化空树 + @root = nil + end + + ### 获取二叉树根节点 ### + def get_root + @root + end + + ### 查找节点 ### + def search(num) + cur = @root + + # 循环查找,越过叶节点后跳出 + while !cur.nil? + # 目标节点在 cur 的右子树中 + if cur.val < num + cur = cur.right + # 目标节点在 cur 的左子树中 + elsif cur.val > num + cur = cur.left + # 找到目标节点,跳出循环 + else + break + end + end + + cur + end + + ### 插入节点 ### + def insert(num) + # 若树为空,则初始化根节点 + if @root.nil? + @root = TreeNode.new(num) + return + end + + # 循环查找,越过叶节点后跳出 + cur, pre = @root, nil + while !cur.nil? + # 找到重复节点,直接返回 + return if cur.val == num + + pre = cur + # 插入位置在 cur 的右子树中 + if cur.val < num + cur = cur.right + # 插入位置在 cur 的左子树中 + else + cur = cur.left + end + end + + # 插入节点 + node = TreeNode.new(num) + if pre.val < num + pre.right = node + else + pre.left = node + end + end + + ### 删除节点 ### + def remove(num) + # 若树为空,直接提前返回 + return if @root.nil? + + # 循环查找,越过叶节点后跳出 + cur, pre = @root, nil + while !cur.nil? + # 找到待删除节点,跳出循环 + break if cur.val == num + + pre = cur + # 待删除节点在 cur 的右子树中 + if cur.val < num + cur = cur.right + # 待删除节点在 cur 的左子树中 + else + cur = cur.left + end + end + # 若无待删除节点,则直接返回 + return if cur.nil? + + # 子节点数量 = 0 or 1 + if cur.left.nil? || cur.right.nil? + # 当子节点数量 = 0 / 1 时, child = null / 该子节点 + child = cur.left || cur.right + # 删除节点 cur + if cur != @root + if pre.left == cur + pre.left = child + else + pre.right = child + end + else + # 若删除节点为根节点,则重新指定根节点 + @root = child + end + # 子节点数量 = 2 + else + # 获取中序遍历中 cur 的下一个节点 + tmp = cur.right + while !tmp.left.nil? + tmp = tmp.left + end + # 递归删除节点 tmp + remove(tmp.val) + # 用 tmp 覆盖 cur + cur.val = tmp.val + end + end +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化二叉搜索树 + bst = BinarySearchTree.new + nums = [8, 4, 12, 2, 6, 10, 14, 1, 3, 5, 7, 9, 11, 13, 15] + # 请注意,不同的插入顺序会生成不同的二叉树,该序列可以生成一个完美二叉树 + nums.each { |num| bst.insert(num) } + puts "\n初始化的二叉树为\n" + print_tree(bst.get_root) + + # 查找节点 + node = bst.search(7) + puts "\n查找到的节点对象为: #{node},节点值 = #{node.val}" + + # 插入节点 + bst.insert(16) + puts "\n插入节点 16 后,二叉树为\n" + print_tree(bst.get_root) + + # 删除节点 + bst.remove(1) + puts "\n删除节点 1 后,二叉树为\n" + print_tree(bst.get_root) + + bst.remove(2) + puts "\n删除节点 2 后,二叉树为\n" + print_tree(bst.get_root) + + bst.remove(4) + puts "\n删除节点 4 后,二叉树为\n" + print_tree(bst.get_root) +end diff --git a/codes/ruby/chapter_tree/binary_tree.rb b/codes/ruby/chapter_tree/binary_tree.rb new file mode 100644 index 000000000..7a6bfa0ba --- /dev/null +++ b/codes/ruby/chapter_tree/binary_tree.rb @@ -0,0 +1,38 @@ +=begin +File: binary_tree.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### Driver Code ### +if __FILE__ == $0 + # 初始化二叉树 + # 初始化节点 + n1 = TreeNode.new(1) + n2 = TreeNode.new(2) + n3 = TreeNode.new(3) + n4 = TreeNode.new(4) + n5 = TreeNode.new(5) + # 构建节点之间的引用(指针) + n1.left = n2 + n1.right = n3 + n2.left = n4 + n2.right = n5 + puts "\n初始化二叉树\n\n" + print_tree(n1) + + # 插入与删除节点 + _p = TreeNode.new(0) + # 在 n1 -> n2 中间插入节点 _p + n1.left = _p + _p.left = n2 + puts "\n插入节点 _p 后\n\n" + print_tree(n1) + # 删除节点 + n1.left = n2 + puts "\n删除节点 _p 后\n\n" + print_tree(n1) +end diff --git a/codes/ruby/chapter_tree/binary_tree_bfs.rb b/codes/ruby/chapter_tree/binary_tree_bfs.rb new file mode 100644 index 000000000..1934b0c1c --- /dev/null +++ b/codes/ruby/chapter_tree/binary_tree_bfs.rb @@ -0,0 +1,36 @@ +=begin +File: binary_tree_bfs.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 层序遍历 ### +def level_order(root) + # 初始化队列,加入根节点 + queue = [root] + # 初始化一个列表,用于保存遍历序列 + res = [] + while !queue.empty? + node = queue.shift # 队列出队 + res << node.val # 保存节点值 + queue << node.left unless node.left.nil? # 左子节点入队 + queue << node.right unless node.right.nil? # 右子节点入队 + end + res +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化二叉树 + # 这里借助了一个从数组直接生成二叉树的函数 + root = arr_to_tree([1, 2, 3, 4, 5, 6, 7]) + puts "\n初始化二叉树\n\n" + print_tree(root) + + # 层序遍历 + res = level_order(root) + puts "\n层序遍历的节点打印序列 = #{res}" +end diff --git a/codes/ruby/chapter_tree/binary_tree_dfs.rb b/codes/ruby/chapter_tree/binary_tree_dfs.rb new file mode 100644 index 000000000..35bfc2589 --- /dev/null +++ b/codes/ruby/chapter_tree/binary_tree_dfs.rb @@ -0,0 +1,62 @@ +=begin +File: binary_tree_dfs.rb +Created Time: 2024-04-18 +Author: Xuan Khoa Tu Nguyen (ngxktuzkai2000@gmail.com) +=end + +require_relative '../utils/tree_node' +require_relative '../utils/print_util' + +### 前序遍历 ### +def pre_oder(root) + return if root.nil? + + # 访问优先级:根节点 -> 左子树 -> 右子树 + $res << root.val + pre_oder(root.left) + pre_oder(root.right) +end + +### 中序遍历 ### +def in_order(root) + return if root.nil? + + # 访问优先级:左子树 -> 根节点 -> 右子树 + in_order(root.left) + $res << root.val + in_order(root.right) +end + +### 后序遍历 ### +def post_order(root) + return if root.nil? + + # 访问优先级:左子树 -> 右子树 -> 根节点 + post_order(root.left) + post_order(root.right) + $res << root.val +end + +### Driver Code ### +if __FILE__ == $0 + # 初始化二叉树 + # 这里借助了一个从数组直接生成二叉树的函数 + root = arr_to_tree([1, 2, 3, 4, 5, 6, 7]) + puts "\n初始化二叉树\n\n" + print_tree(root) + + # 前序遍历 + $res = [] + pre_oder(root) + puts "\n前序遍历的节点打印序列 = #{$res}" + + # 中序遍历 + $res.clear + in_order(root) + puts "\nn中序遍历的节点打印序列 = #{$res}" + + # 后序遍历 + $res.clear + post_order(root) + puts "\nn后序遍历的节点打印序列 = #{$res}" +end diff --git a/codes/ruby/utils/tree_node.rb b/codes/ruby/utils/tree_node.rb index da796afb2..58f098996 100644 --- a/codes/ruby/utils/tree_node.rb +++ b/codes/ruby/utils/tree_node.rb @@ -16,3 +16,38 @@ class TreeNode @height = 0 end end + +### 将列表反序列化为二叉数树:递归 ### +def arr_to_tree_dfs(arr, i) + # 如果索引超出数组长度,或者对应的元素为 nil ,则返回 nil + return if i < 0 || i >= arr.length || arr[i].nil? + # 构建当前节点 + root = TreeNode.new(arr[i]) + # 递归构建左右子树 + root.left = arr_to_tree_dfs(arr, 2 * i + 1) + root.right = arr_to_tree_dfs(arr, 2 * i + 2) + root +end + +### 将列表反序列化为二叉树 ### +def arr_to_tree(arr) + arr_to_tree_dfs(arr, 0) +end + +### 将二叉树序列化为列表:递归 ### +def tree_to_arr_dfs(root, i, res) + return if root.nil? + + res += Array.new(i - res.length + 1) if i >= res.length + res[i] = root.val + + tree_to_arr_dfs(root.left, 2 * i + 1, res) + tree_to_arr_dfs(root.right, 2 * i + 2, res) +end + +### 将二叉树序列化为列表 ### +def tree_to_arr(root) + res = [] + tree_to_arr_dfs(root, 0, res) + res +end