红黑树可视化工具
目录
一、引言
二、红黑树核心数据结构功能
三、可视化与交互功能
四、辅助功能
五、红黑树可视化工具的Python代码完整实现
六、程序运行的部分截图展示
七、总结
一、引言
本文的Python代码实现了一个功能完整的红黑树可视化工具,有关红黑树的详细讲解可以参考【数据结构】红黑树的基本操作-CSDN博客。该工具不仅实现了红黑树的全部核心算法(插入 / 删除 / 平衡修复等),还通过可视化界面和动画效果,将抽象的树结构操作转化为直观的视觉过程,适合用于学习红黑树的原理和实现逻辑。无论是数据结构初学者还是需要深入理解平衡树机制的开发者,都能通过该工具直观感受红黑树的自平衡特性和操作流程。同时,教师可以通过该工具辅助教学,让学生更加深入理解红黑树的原理和实现逻辑。该工具也可以在红黑树的可视化工具-红黑树的动画展示资源-CSDN下载 直接下载使用。
二、红黑树核心数据结构功能
基于红黑树的五大性质(节点非红即黑、根节点为黑、叶节点为黑、红节点子节点必为黑、任一节点到叶节点的黑节点数相同),实现了完整的红黑树操作:
-
基本操作
- 插入(Insert):支持插入键值对,自动触发平衡修复(通过变色和旋转),确保插入后仍满足红黑树性质。
- 删除(Delete):支持删除指定键,通过移植(transplant)操作移除节点,并触发删除修复,维持树的平衡。
- 查找(Search):根据键查找对应节点,返回对应值;支持判断节点是否存在。
- 更新(Update):修改指定键对应的节点值,不改变树结构。
-
遍历功能
- 支持四种经典遍历方式,并通过动画展示遍历过程:
- 前序遍历(根→左→右)
- 中序遍历(左→根→右)
- 后序遍历(左→右→根)
- 层序遍历(按层次从上到下、从左到右)
- 支持四种经典遍历方式,并通过动画展示遍历过程:
-
节点关系查询
- 前驱 / 后继节点:查找指定节点的前驱(比其小的最大节点)和后继(比其大的最小节点)。
- 最大 / 最小值:查找树中键值最大和最小的节点。
-
范围查询
- 支持指定区间
[min_key, max_key]
的节点查询,返回区间内所有键值对。 - 统计区间内节点的数量,无需返回具体节点。
- 支持指定区间
-
红黑树性质校验
- 自动检查红黑树的五大性质是否满足,定位违反性质的节点并高亮显示,辅助理解平衡机制。
-
树状态管理
- 清空树:逐个删除所有节点,展示删除动画。
- 树信息统计:获取树的大小(节点数量)和高度。
三、可视化与交互功能
通过 Tkinter 构建的 GUI 界面,将红黑树的抽象操作转化为直观的视觉展示:
-
界面组成
- 左侧代码区:展示红黑树的 C++ 实现代码,便于对照学习数据结构的底层逻辑。
- 右侧可视化区:包含画布(展示红黑树图形)、控制面板(操作按钮)和状态条(显示当前操作信息)。
-
交互控制面板
- 提供一站式操作按钮,包括:
- 基础操作:插入、删除、查找、更新节点。
- 关系查询:查找前驱、后继、最大 / 最小值。
- 范围操作:区间查询、区间计数。
- 辅助功能:检查红黑树性质、清空树、运行示例。
- 动画速度调节:通过滑块控制操作过程的动画延迟(100-2000 毫秒),适应不同学习节奏。
- 提供一站式操作按钮,包括:
-
动画与高亮效果
- 操作过程可视化:所有操作(插入、删除、旋转等)均通过动画分步展示,如:
- 插入时的查找路径动画、修复阶段的变色和旋转动画。
- 删除时的节点移植动画、修复阶段的平衡调整动画。
- 节点高亮:通过不同颜色标记节点状态,增强操作理解:
- 查找路径节点(浅黄色)、当前操作节点(浅蓝色)、父节点(浅橙色)、祖父节点(浅紫色)等。
- 违反红黑树性质的节点(红色)、已找到的节点(浅绿色)等。
- 操作过程可视化:所有操作(插入、删除、旋转等)均通过动画分步展示,如:
-
示例演示
- 内置示例流程,自动执行一系列操作(插入 7 个示例节点、测试查找 / 更新 / 遍历 / 删除 / 范围查询等),展示红黑树的完整使用场景,适合初学者快速理解。
四、辅助功能
- 中文支持:通过设置支持中文的字体(SimHei),确保界面文字和提示信息正常显示。
- 线程处理:所有耗时操作(如插入、删除、遍历)均在独立线程中执行,避免 UI 冻结,保证动画流畅。
- 错误处理:对无效输入(如非整数键值)提供友好的错误提示,增强工具的可用性。
五、红黑树可视化工具的Python代码完整实现
import tkinter as tk
from tkinter import ttk, scrolledtext, messagebox, simpledialog
import time
import threading
from tkinter import font# 红黑树节点颜色
BLACK = 0
RED = 1class RBNode:def __init__(self, key, value):self.key = keyself.value = valueself.color = RED # 新节点默认为红色self.left = Noneself.right = Noneself.parent = None# 可视化相关属性self.x = 0self.y = 0# 用于动画的临时属性self.anim_x = 0self.anim_y = 0class RedBlackTree:def __init__(self, visualizer=None):self.nil = RBNode(0, None)self.nil.color = BLACKself.nil.left = self.nil # 哨兵节点的左右孩子指向自身self.nil.right = self.nilself.nil.parent = self.nilself.root = self.nilself.size_val = 0 # 重命名属性,避免与方法冲突self.visualizer = visualizer # 可视化器引用self.delay = 1000 # 动画延迟,毫秒self.animation_step = 50 # 动画步长(毫秒)def set_visualizer(self, visualizer):self.visualizer = visualizerdef set_delay(self, delay):self.delay = delayself.animation_step = max(10, delay // 20) # 动画步长为延迟的1/20,最小10msdef left_rotate(self, x):if x == self.nil:returnif self.visualizer:self.visualizer.highlight_node(x, "rotate")self.visualizer.update_status("执行左旋操作 on " + str(x.key))self.visualizer.update()time.sleep(self.delay / 1000)y = x.rightif y == self.nil: # 确保y不是哨兵节点returnx.right = y.leftif y.left != self.nil:y.left.parent = xy.parent = x.parentif x.parent == self.nil:self.root = yelif x == x.parent.left:x.parent.left = yelse:x.parent.right = yy.left = xx.parent = y# 动画旋转效果if self.visualizer:self.visualizer.animate_rotation(x, y, "left")self.visualizer.update_tree_layout()self.visualizer.update_status("完成左旋操作 on " + str(x.key))self.visualizer.update()time.sleep(self.delay / 1000)self.visualizer.clear_highlights()def right_rotate(self, y):if y == self.nil:returnif self.visualizer:self.visualizer.highlight_node(y, "rotate")self.visualizer.update_status("执行右旋操作 on " + str(y.key))self.visualizer.update()time.sleep(self.delay / 1000)x = y.leftif x == self.nil: # 确保x不是哨兵节点returny.left = x.rightif x.right != self.nil:x.right.parent = yx.parent = y.parentif y.parent == self.nil:self.root = xelif y == y.parent.right:y.parent.right = xelse:y.parent.left = xx.right = yy.parent = x# 动画旋转效果if self.visualizer:self.visualizer.animate_rotation(y, x, "right")self.visualizer.update_tree_layout()self.visualizer.update_status("完成右旋操作 on " + str(y.key))self.visualizer.update()time.sleep(self.delay / 1000)self.visualizer.clear_highlights()def insert(self, key, value=None):if value is None:value = str(key)z = RBNode(key, value)z.left = self.nil # 使用哨兵节点而非Nonez.right = self.nilz.parent = self.nily = self.nilx = self.rootif self.visualizer:self.visualizer.update_status(f"插入节点 {key}")self.visualizer.clear_highlights()# 查找插入位置while x != self.nil:y = xif z.key < x.key:x = x.leftelse:x = x.rightif self.visualizer:self.visualizer.highlight_node(x, "search")self.visualizer.highlight_node(y, "parent")self.visualizer.update_status(f"查找插入位置: 比较 {key} 与 {x.key if x != self.nil else 'NIL'}")self.visualizer.update()time.sleep(self.delay / 1000)z.parent = yif y == self.nil:self.root = zif self.visualizer:self.visualizer.update_status(f"树为空,{key} 成为根节点")elif z.key < y.key:y.left = zif self.visualizer:self.visualizer.update_status(f"将 {key} 插入为 {y.key} 的左子节点")else:y.right = zif self.visualizer:self.visualizer.update_status(f"将 {key} 插入为 {y.key} 的右子节点")z.color = REDself.size_val += 1 # 修改属性名# 更新可视化if self.visualizer:self.visualizer.update_tree_layout()self.visualizer.highlight_node(z, "insert")self.visualizer.update()time.sleep(self.delay / 1000)# 修复红黑树性质if z.parent != self.nil: # 如果不是根节点self.insert_fixup(z)# 确保根节点始终为黑色self.root.color = BLACKif self.visualizer:self.visualizer.clear_highlights()self.visualizer.update_status(f"完成插入节点 {key}")self.visualizer.update()def insert_fixup(self, z):if self.visualizer:self.visualizer.update_status("开始插入修复操作")self.visualizer.update()time.sleep(self.delay / 1000)# 当父节点为红色时,违反“红节点的子节点必为黑”的性质,需要修复while z.parent.color == RED:# 确保祖父节点存在(因为父节点是红色,不可能是根节点)if z.parent.parent == self.nil:breakif z.parent == z.parent.parent.left:# 获取叔节点y = z.parent.parent.rightif self.visualizer:self.visualizer.highlight_node(z, "current")self.visualizer.highlight_node(z.parent, "parent")self.visualizer.highlight_node(z.parent.parent, "grandparent")self.visualizer.highlight_node(y, "uncle")# 安全地获取叔节点的keyuncle_key = y.key if y != self.nil else "NIL"self.visualizer.update_status(f"插入修复: 当前节点 {z.key}, 父节点 {z.parent.key}, 祖父节点 {z.parent.parent.key}, 叔节点 {uncle_key}")self.visualizer.update()time.sleep(self.delay / 1000)if y.color == RED:# 情况1:叔节点为红色self.visualizer.update_status("情况1: 叔节点为红色 - 执行变色操作")z.parent.color = BLACKy.color = BLACKz.parent.parent.color = REDz = z.parent.parentif self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)else:# 情况2:z是右孩子if z == z.parent.right:self.visualizer.update_status("情况2: 当前节点是右孩子 - 执行左旋")z = z.parentself.left_rotate(z)# 情况3:z是左孩子self.visualizer.update_status("情况3: 当前节点是左孩子 - 执行变色和右旋")z.parent.color = BLACKz.parent.parent.color = REDself.right_rotate(z.parent.parent)else:# 获取叔节点y = z.parent.parent.leftif self.visualizer:self.visualizer.highlight_node(z, "current")self.visualizer.highlight_node(z.parent, "parent")self.visualizer.highlight_node(z.parent.parent, "grandparent")self.visualizer.highlight_node(y, "uncle")# 安全地获取叔节点的keyuncle_key = y.key if y != self.nil else "NIL"self.visualizer.update_status(f"插入修复: 当前节点 {z.key}, 父节点 {z.parent.key}, 祖父节点 {z.parent.parent.key}, 叔节点 {uncle_key}")self.visualizer.update()time.sleep(self.delay / 1000)if y.color == RED:# 情况1:叔节点为红色self.visualizer.update_status("情况1: 叔节点为红色 - 执行变色操作")z.parent.color = BLACKy.color = BLACKz.parent.parent.color = REDz = z.parent.parentif self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)else:# 情况2:z是左孩子if z == z.parent.left:self.visualizer.update_status("情况2: 当前节点是左孩子 - 执行右旋")z = z.parentself.right_rotate(z)# 情况3:z是右孩子self.visualizer.update_status("情况3: 当前节点是右孩子 - 执行变色和左旋")z.parent.color = BLACKz.parent.parent.color = REDself.left_rotate(z.parent.parent)if z == self.root:breakself.root.color = BLACKif self.visualizer:self.visualizer.update_status("完成插入修复操作")self.visualizer.update()def transplant(self, u, v):if self.visualizer:self.visualizer.update_status(f"执行移植操作: 用 {v.key if v != self.nil else 'NIL'} 替换 {u.key}")self.visualizer.update()time.sleep(self.delay / 1000)if u.parent == self.nil:self.root = velif u == u.parent.left:u.parent.left = velse:u.parent.right = vv.parent = u.parentif self.visualizer:self.visualizer.highlight_node(u, "delete")self.visualizer.highlight_node(v, "transplant")self.visualizer.animate_transplant(u, v)self.visualizer.update()time.sleep(self.delay / 1000)def minimum(self, node):if node == self.nil:return self.nilif self.visualizer:self.visualizer.update_status(f"查找 {node.key} 子树中的最小值")self.visualizer.clear_highlights()while node.left != self.nil:node = node.leftif self.visualizer:self.visualizer.highlight_node(node, "search")self.visualizer.update_status(f"最小值查找: 移动到左子节点 {node.key}")self.visualizer.update()time.sleep(self.delay / 2000)if self.visualizer:self.visualizer.highlight_node(node, "minimum")self.visualizer.update_status(f"找到最小值 {node.key}")self.visualizer.update()time.sleep(self.delay / 1000)return nodedef maximum(self, node):if node == self.nil:return self.nilif self.visualizer:self.visualizer.update_status(f"查找 {node.key} 子树中的最大值")self.visualizer.clear_highlights()while node.right != self.nil:node = node.rightif self.visualizer:self.visualizer.highlight_node(node, "search")self.visualizer.update_status(f"最大值查找: 移动到右子节点 {node.key}")self.visualizer.update()time.sleep(self.delay / 2000)if self.visualizer:self.visualizer.highlight_node(node, "maximum")self.visualizer.update_status(f"找到最大值 {node.key}")self.visualizer.update()time.sleep(self.delay / 1000)return nodedef delete(self, key):if self.visualizer:self.visualizer.update_status(f"删除节点 {key}")self.visualizer.clear_highlights()z = self.search_node(key)if z == self.nil:if self.visualizer:self.visualizer.update_status(f"节点 {key} 不存在,删除失败")self.visualizer.update()return Falseif self.visualizer:self.visualizer.highlight_node(z, "delete")self.visualizer.update()time.sleep(self.delay / 1000)y = zy_original_color = y.colorif z.left == self.nil:x = z.rightself.transplant(z, z.right)elif z.right == self.nil:x = z.leftself.transplant(z, z.left)else:y = self.minimum(z.right)y_original_color = y.colorx = y.rightif y.parent == z:x.parent = yelse:self.transplant(y, y.right)y.right = z.righty.right.parent = y# 用后继 y 替换 zself.transplant(z, y)y.left = z.lefty.left.parent = yy.color = z.colorself.size_val -= 1 # 修改属性名if y_original_color == BLACK:self.delete_fixup(x)if self.visualizer:self.visualizer.update_tree_layout()self.visualizer.clear_highlights()self.visualizer.update_status(f"完成删除节点 {key}")self.visualizer.update()return Truedef delete_fixup(self, x):if self.visualizer:self.visualizer.update_status("开始删除修复操作")self.visualizer.update()time.sleep(self.delay / 1000)# 当 x 为黑且非根时,可能违反“路径黑节点数相同”的性质,需要修复while x != self.root and x.color == BLACK:if x == x.parent.left:# 获取兄弟节点w = x.parent.rightif self.visualizer:self.visualizer.highlight_node(x, "current")self.visualizer.highlight_node(x.parent, "parent")self.visualizer.highlight_node(w, "sibling")# 安全地获取兄弟节点的keysibling_key = w.key if w != self.nil else "NIL"self.visualizer.update_status(f"删除修复: 当前节点 {x.key if x != self.nil else 'NIL'}, 父节点 {x.parent.key}, 兄弟节点 {sibling_key}")self.visualizer.update()time.sleep(self.delay / 1000)if w.color == RED:# 情况1:兄弟是红色self.visualizer.update_status("情况1: 兄弟节点为红色 - 执行变色和左旋")w.color = BLACKx.parent.color = REDself.left_rotate(x.parent)w = x.parent.rightif self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)if w.left.color == BLACK and w.right.color == BLACK:# 情况2:兄弟的两个孩子都是黑色self.visualizer.update_status("情况2: 兄弟节点的两个孩子都是黑色 - 兄弟节点变红")w.color = REDx = x.parentif self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)else:if w.right.color == BLACK:# 情况3:兄弟的右孩子是黑色,左孩子是红色self.visualizer.update_status("情况3: 兄弟节点右孩子为黑色 - 执行变色和右旋")w.left.color = BLACKw.color = REDself.right_rotate(w)w = x.parent.rightif self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)# 情况4:兄弟的右孩子是红色self.visualizer.update_status("情况4: 兄弟节点右孩子为红色 - 执行变色和左旋")w.color = x.parent.colorx.parent.color = BLACKw.right.color = BLACKself.left_rotate(x.parent)x = self.root # 结束循环if self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)else:# 获取兄弟节点w = x.parent.leftif self.visualizer:self.visualizer.highlight_node(x, "current")self.visualizer.highlight_node(x.parent, "parent")self.visualizer.highlight_node(w, "sibling")# 安全地获取兄弟节点的keysibling_key = w.key if w != self.nil else "NIL"self.visualizer.update_status(f"删除修复: 当前节点 {x.key if x != self.nil else 'NIL'}, 父节点 {x.parent.key}, 兄弟节点 {sibling_key}")self.visualizer.update()time.sleep(self.delay / 1000)if w.color == RED:# 情况1:兄弟是红色self.visualizer.update_status("情况1: 兄弟节点为红色 - 执行变色和右旋")w.color = BLACKx.parent.color = REDself.right_rotate(x.parent)w = x.parent.leftif self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)if w.right.color == BLACK and w.left.color == BLACK:# 情况2:兄弟的两个孩子都是黑色self.visualizer.update_status("情况2: 兄弟节点的两个孩子都是黑色 - 兄弟节点变红")w.color = REDx = x.parentif self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)else:if w.left.color == BLACK:# 情况3:兄弟的左孩子是黑色,右孩子是红色self.visualizer.update_status("情况3: 兄弟节点左孩子为黑色 - 执行变色和左旋")w.right.color = BLACKw.color = REDself.left_rotate(w)w = x.parent.leftif self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)# 情况4:兄弟的左孩子是红色self.visualizer.update_status("情况4: 兄弟节点左孩子为红色 - 执行变色和右旋")w.color = x.parent.colorx.parent.color = BLACKw.left.color = BLACKself.right_rotate(x.parent)x = self.root # 结束循环if self.visualizer:self.visualizer.update()time.sleep(self.delay / 1000)x.color = BLACKif self.visualizer:self.visualizer.update_status("完成删除修复操作")self.visualizer.update()def search_node(self, key):current = self.rootif self.visualizer:self.visualizer.clear_highlights()self.visualizer.update_status(f"查找节点 {key}")while current != self.nil:if self.visualizer:self.visualizer.highlight_node(current, "search")self.visualizer.update_status(f"比较 {key} 与 {current.key}")self.visualizer.update()time.sleep(self.delay / 2000)if key == current.key:if self.visualizer:self.visualizer.highlight_node(current, "found")self.visualizer.update_status(f"找到节点 {key}")self.visualizer.update()time.sleep(self.delay / 1000)return currentelif key < current.key:current = current.leftelse:current = current.rightif self.visualizer:self.visualizer.update_status(f"未找到节点 {key}")self.visualizer.update()return self.nildef search(self, key):node = self.search_node(key)if node != self.nil:return node.valuereturn Nonedef update(self, key, new_value):if self.visualizer:self.visualizer.update_status(f"更新节点 {key} 的值为 {new_value}")self.visualizer.clear_highlights()node = self.search_node(key)if node != self.nil:old_value = node.valuenode.value = new_valueif self.visualizer:self.visualizer.highlight_node(node, "update")self.visualizer.update_status(f"节点 {key} 的值已从 {old_value} 更新为 {new_value}")self.visualizer.update()time.sleep(self.delay / 1000)self.visualizer.clear_highlights()return Trueif self.visualizer:self.visualizer.update_status(f"更新失败,节点 {key} 不存在")self.visualizer.update()return Falsedef inorder(self):result = []self._inorder_helper(self.root, result)return resultdef _inorder_helper(self, node, result):if node != self.nil:self._inorder_helper(node.left, result)result.append((node.key, node.value))self._inorder_helper(node.right, result)def preorder(self):result = []self._preorder_helper(self.root, result)return resultdef _preorder_helper(self, node, result):if node != self.nil:result.append((node.key, node.value))self._preorder_helper(node.left, result)self._preorder_helper(node.right, result)def postorder(self):result = []self._postorder_helper(self.root, result)return resultdef _postorder_helper(self, node, result):if node != self.nil:self._postorder_helper(node.left, result)self._postorder_helper(node.right, result)result.append((node.key, node.value))def levelorder(self):result = []if self.root == self.nil:return resultqueue = [self.root]while queue:node = queue.pop(0)result.append((node.key, node.value))if node.left != self.nil:queue.append(node.left)if node.right != self.nil:queue.append(node.right)return resultdef find_min(self):if self.root == self.nil:return Nonereturn self.minimum(self.root)def find_max(self):if self.root == self.nil:return Nonereturn self.maximum(self.root)def find_predecessor(self, key):node = self.search_node(key)if node == self.nil:return Noneif self.visualizer:self.visualizer.update_status(f"查找 {key} 的前驱节点")self.visualizer.update()time.sleep(self.delay / 1000)# 左子树非空:前驱是左子树的最大值if node.left != self.nil:pred = self.maximum(node.left)if self.visualizer:self.visualizer.highlight_node(node, "current")self.visualizer.highlight_node(pred, "predecessor")self.visualizer.update_status(f"{key} 的前驱是左子树最大值 {pred.key}")self.visualizer.update()time.sleep(self.delay / 1000)return pred# 左子树为空:向上找第一个有右子树的祖先pred = node.parentif self.visualizer:self.visualizer.highlight_node(node, "current")while pred != self.nil and node == pred.left:if self.visualizer:self.visualizer.highlight_node(pred, "search")self.visualizer.update_status(f"向上查找前驱: 检查 {pred.key}")self.visualizer.update()time.sleep(self.delay / 1000)node = predpred = pred.parentif pred != self.nil:if self.visualizer:self.visualizer.highlight_node(pred, "predecessor")self.visualizer.update_status(f"{key} 的前驱是 {pred.key}")self.visualizer.update()time.sleep(self.delay / 1000)return predif self.visualizer:self.visualizer.update_status(f"{key} 没有前驱节点")self.visualizer.update()return Nonedef find_successor(self, key):node = self.search_node(key)if node == self.nil:return Noneif self.visualizer:self.visualizer.update_status(f"查找 {key} 的后继节点")self.visualizer.update()time.sleep(self.delay / 1000)# 右子树非空:后继是右子树的最小值if node.right != self.nil:succ = self.minimum(node.right)if self.visualizer:self.visualizer.highlight_node(node, "current")self.visualizer.highlight_node(succ, "successor")self.visualizer.update_status(f"{key} 的后继是右子树最小值 {succ.key}")self.visualizer.update()time.sleep(self.delay / 1000)return succ# 右子树为空:向上找第一个有左子树的祖先succ = node.parentif self.visualizer:self.visualizer.highlight_node(node, "current")while succ != self.nil and node == succ.right:if self.visualizer:self.visualizer.highlight_node(succ, "search")self.visualizer.update_status(f"向上查找后继: 检查 {succ.key}")self.visualizer.update()time.sleep(self.delay / 1000)node = succsucc = succ.parentif succ != self.nil:if self.visualizer:self.visualizer.highlight_node(succ, "successor")self.visualizer.update_status(f"{key} 的后继是 {succ.key}")self.visualizer.update()time.sleep(self.delay / 1000)return succif self.visualizer:self.visualizer.update_status(f"{key} 没有后继节点")self.visualizer.update()return Nonedef range_query(self, min_key, max_key):result = []if self.visualizer:self.visualizer.update_status(f"范围查询: [{min_key}, {max_key}]")self.visualizer.clear_highlights()self.visualizer.update()time.sleep(self.delay / 1000)self._range_query_helper(self.root, min_key, max_key, result)if self.visualizer:self.visualizer.update_status(f"范围查询完成,找到 {len(result)} 个节点")self.visualizer.update()return resultdef _range_query_helper(self, node, min_key, max_key, result):if node == self.nil:returnif self.visualizer:self.visualizer.highlight_node(node, "search")self.visualizer.update_status(f"范围查询中: 检查 {node.key}")self.visualizer.update()time.sleep(self.delay / 2000)# 左子树可能有符合条件的节点if node.key > min_key:self._range_query_helper(node.left, min_key, max_key, result)# 当前节点在范围内,加入结果if node.key >= min_key and node.key <= max_key:result.append((node.key, node.value))if self.visualizer:self.visualizer.highlight_node(node, "found")self.visualizer.update_status(f"找到范围内节点: {node.key}")self.visualizer.update()time.sleep(self.delay / 2000)# 右子树可能有符合条件的节点if node.key < max_key:self._range_query_helper(node.right, min_key, max_key, result)def count_range(self, min_key, max_key):count = self._count_range_helper(self.root, min_key, max_key)if self.visualizer:self.visualizer.update_status(f"区间 [{min_key}, {max_key}] 内共有 {count} 个节点")self.visualizer.update()return countdef _count_range_helper(self, node, min_key, max_key):if node == self.nil:return 0count = 0# 当前节点在范围内,计数+1if node.key >= min_key and node.key <= max_key:count = 1# 左子树递归统计if node.key > min_key:count += self._count_range_helper(node.left, min_key, max_key)# 右子树递归统计if node.key < max_key:count += self._count_range_helper(node.right, min_key, max_key)return countdef check_properties(self):if self.visualizer:self.visualizer.update_status("检查红黑树性质...")self.visualizer.update()time.sleep(self.delay / 1000)valid = True# 性质1:节点颜色不是红色就是黑色if not self._check_color_property(self.root):if self.visualizer:self.visualizer.update_status("违反性质1:节点颜色不是红色或黑色")self.visualizer.update()valid = False# 性质2:根节点是黑色if self.root.color != BLACK:if self.visualizer:self.visualizer.highlight_node(self.root, "error")self.visualizer.update_status("违反性质2:根节点不是黑色")self.visualizer.update()valid = False# 性质4:如果一个节点是红色,则它的两个子节点都是黑色if not self._check_red_parent_property(self.root):if self.visualizer:self.visualizer.update_status("违反性质4:存在红色节点的子节点为红色")self.visualizer.update()valid = False# 性质5:从任一节点到其每个叶子的所有简单路径都包含相同数目的黑色节点if self._black_height(self.root) == -1:if self.visualizer:self.visualizer.update_status("违反性质5:从根到叶的路径黑节点数不同")self.visualizer.update()valid = Falseif valid and self.visualizer:self.visualizer.update_status("红黑树所有性质均满足")self.visualizer.update()return validdef _check_color_property(self, node):if node == self.nil:return Trueif node.color != RED and node.color != BLACK:return Falsereturn self._check_color_property(node.left) and self._check_color_property(node.right)def _check_red_parent_property(self, node):if node == self.nil:return Trueif node.color == RED:if node.left.color == RED or node.right.color == RED:if self.visualizer:self.visualizer.highlight_node(node, "error")self.visualizer.highlight_node(node.left if node.left.color == RED else node.right, "error")self.visualizer.update()time.sleep(self.delay / 1000)return Falsereturn self._check_red_parent_property(node.left) and self._check_red_parent_property(node.right)def _black_height(self, node):if node == self.nil:return 1 # 外部节点(nil)的黑高为1left_bh = self._black_height(node.left)right_bh = self._black_height(node.right)if left_bh == -1 or right_bh == -1 or left_bh != right_bh:if self.visualizer and left_bh != right_bh:self.visualizer.highlight_node(node, "error")self.visualizer.highlight_node(node.left, "error")self.visualizer.highlight_node(node.right, "error")self.visualizer.update()time.sleep(self.delay / 1000)return -1return left_bh + (1 if node.color == BLACK else 0)def clear(self):if self.visualizer:self.visualizer.update_status("清空树中所有节点")self.visualizer.update()time.sleep(self.delay / 1000)# 逐个删除节点以展示动画nodes = self.inorder()for key, _ in nodes:self.delete(key)time.sleep(self.delay / 2000)self.root = self.nilself.size_val = 0 # 修改属性名if self.visualizer:self.visualizer.update_status("树已清空")self.visualizer.update()# 修复size()方法,避免与属性名冲突def size(self):return self.size_val# 添加height()方法def height(self):return self._height_helper(self.root)def _height_helper(self, node):if node == self.nil:return 0left_height = self._height_helper(node.left)right_height = self._height_helper(node.right)return 1 + max(left_height, right_height)class RedBlackTreeVisualizer:def __init__(self, root, rbt):self.root = rootself.rbt = rbtself.rbt.set_visualizer(self)# 创建主框架self.main_frame = ttk.Frame(root)self.main_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)# 左侧:C++代码显示self.code_frame = ttk.LabelFrame(self.main_frame, text="C++ 红黑树代码")self.code_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)self.code_text = scrolledtext.ScrolledText(self.code_frame, wrap=tk.WORD, font=("Consolas", 10))self.code_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)self.code_text.config(state=tk.DISABLED)# 右侧:可视化和控制面板self.visual_frame = ttk.Frame(self.main_frame)self.visual_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=5, pady=5)# 状态条self.status_var = tk.StringVar()self.status_var.set("就绪")self.status_bar = ttk.Label(self.visual_frame, textvariable=self.status_var, relief=tk.SUNKEN, anchor=tk.W)self.status_bar.pack(side=tk.BOTTOM, fill=tk.X, padx=5, pady=2)# 画布区域(带滚动条)self.canvas_frame = ttk.LabelFrame(self.visual_frame, text="红黑树可视化")self.canvas_frame.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)# 创建滚动条框架self.scroll_frame = ttk.Frame(self.canvas_frame)self.scroll_frame.pack(fill=tk.BOTH, expand=True)# 垂直滚动条self.vscrollbar = ttk.Scrollbar(self.scroll_frame, orient=tk.VERTICAL)self.vscrollbar.pack(side=tk.RIGHT, fill=tk.Y)# 水平滚动条self.hscrollbar = ttk.Scrollbar(self.scroll_frame, orient=tk.HORIZONTAL)self.hscrollbar.pack(side=tk.BOTTOM, fill=tk.X)# 创建画布并关联滚动条self.canvas = tk.Canvas(self.scroll_frame, bg="white",yscrollcommand=self.vscrollbar.set,xscrollcommand=self.hscrollbar.set)self.canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)# 配置滚动条与画布关联self.vscrollbar.config(command=self.canvas.yview)self.hscrollbar.config(command=self.canvas.xview)# 绑定事件处理滚动self.canvas.bind("<Configure>", self._on_canvas_configure)# 控制区域self.control_frame = ttk.LabelFrame(self.visual_frame, text="操作控制")self.control_frame.pack(fill=tk.X, padx=5, pady=5)# 第一行控制按钮control_row1 = ttk.Frame(self.control_frame)control_row1.pack(fill=tk.X, padx=5, pady=2)ttk.Label(control_row1, text="键值:").pack(side=tk.LEFT, padx=5, pady=5)self.key_entry = ttk.Entry(control_row1, width=10)self.key_entry.pack(side=tk.LEFT, padx=5, pady=5)self.insert_btn = ttk.Button(control_row1, text="插入", command=self.insert_node)self.insert_btn.pack(side=tk.LEFT, padx=5, pady=5)self.delete_btn = ttk.Button(control_row1, text="删除", command=self.delete_node)self.delete_btn.pack(side=tk.LEFT, padx=5, pady=5)self.search_btn = ttk.Button(control_row1, text="查找", command=self.search_node)self.search_btn.pack(side=tk.LEFT, padx=5, pady=5)self.update_btn = ttk.Button(control_row1, text="更新值", command=self.update_node)self.update_btn.pack(side=tk.LEFT, padx=5, pady=5)# 第二行控制按钮control_row2 = ttk.Frame(self.control_frame)control_row2.pack(fill=tk.X, padx=5, pady=2)self.predecessor_btn = ttk.Button(control_row2, text="查找前驱", command=self.find_predecessor)self.predecessor_btn.pack(side=tk.LEFT, padx=5, pady=5)self.successor_btn = ttk.Button(control_row2, text="查找后继", command=self.find_successor)self.successor_btn.pack(side=tk.LEFT, padx=5, pady=5)self.min_btn = ttk.Button(control_row2, text="最小值", command=self.find_min)self.min_btn.pack(side=tk.LEFT, padx=5, pady=5)self.max_btn = ttk.Button(control_row2, text="最大值", command=self.find_max)self.max_btn.pack(side=tk.LEFT, padx=5, pady=5)# 第三行控制按钮control_row3 = ttk.Frame(self.control_frame)control_row3.pack(fill=tk.X, padx=5, pady=2)ttk.Label(control_row3, text="范围查询:").pack(side=tk.LEFT, padx=5, pady=5)self.min_range_entry = ttk.Entry(control_row3, width=8)self.min_range_entry.pack(side=tk.LEFT, padx=2, pady=5)ttk.Label(control_row3, text="至").pack(side=tk.LEFT, padx=2, pady=5)self.max_range_entry = ttk.Entry(control_row3, width=8)self.max_range_entry.pack(side=tk.LEFT, padx=2, pady=5)self.range_btn = ttk.Button(control_row3, text="查询范围", command=self.range_query)self.range_btn.pack(side=tk.LEFT, padx=5, pady=5)self.count_range_btn = ttk.Button(control_row3, text="统计数量", command=self.count_range)self.count_range_btn.pack(side=tk.LEFT, padx=5, pady=5)# 第四行控制按钮control_row4 = ttk.Frame(self.control_frame)control_row4.pack(fill=tk.X, padx=5, pady=2)self.check_btn = ttk.Button(control_row4, text="检查红黑树性质", command=self.check_properties)self.check_btn.pack(side=tk.LEFT, padx=5, pady=5)self.clear_btn = ttk.Button(control_row4, text="清空树", command=self.clear_tree)self.clear_btn.pack(side=tk.LEFT, padx=5, pady=5)self.example_btn = ttk.Button(control_row4, text="运行示例", command=self.run_example)self.example_btn.pack(side=tk.LEFT, padx=5, pady=5)# 延迟控制control_row5 = ttk.Frame(self.control_frame)control_row5.pack(fill=tk.X, padx=5, pady=2)ttk.Label(control_row5, text="动画速度:").pack(side=tk.LEFT, padx=5, pady=5)self.delay_scale = ttk.Scale(control_row5, from_=100, to=2000, orient=tk.HORIZONTAL, length=200)self.delay_scale.set(1000)self.delay_scale.pack(side=tk.LEFT, padx=5, pady=5)self.delay_scale.bind("<Motion>", self.update_delay)# 遍历控制control_row6 = ttk.Frame(self.control_frame)control_row6.pack(fill=tk.X, padx=5, pady=2)ttk.Label(control_row6, text="遍历方式:").pack(side=tk.LEFT, padx=5, pady=5)self.preorder_btn = ttk.Button(control_row6, text="前序", command=lambda: self.traverse("preorder"))self.preorder_btn.pack(side=tk.LEFT, padx=2, pady=5)self.inorder_btn = ttk.Button(control_row6, text="中序", command=lambda: self.traverse("inorder"))self.inorder_btn.pack(side=tk.LEFT, padx=2, pady=5)self.postorder_btn = ttk.Button(control_row6, text="后序", command=lambda: self.traverse("postorder"))self.postorder_btn.pack(side=tk.LEFT, padx=2, pady=5)self.levelorder_btn = ttk.Button(control_row6, text="层序", command=lambda: self.traverse("levelorder"))self.levelorder_btn.pack(side=tk.LEFT, padx=2, pady=5)# 存储高亮节点和连接线self.highlighted_nodes = {}self.highlighted_edges = []# 初始布局self.update_tree_layout()self.draw_tree()# 加载C++代码self.load_cpp_code()# 确保中文显示正常self.setup_fonts()def _on_canvas_configure(self, event):"""当画布大小改变时更新滚动区域"""self.canvas.configure(scrollregion=self.canvas.bbox("all"))def setup_fonts(self):# 设置支持中文的字体default_font = font.nametofont("TkDefaultFont")default_font.configure(family="SimHei", size=10)self.root.option_add("*Font", default_font)def load_cpp_code(self):# 加载完整的C++代码cpp_code = """#include <iostream>
#include <string>
#include <queue>
#include <vector>using namespace std;// 颜色枚举:红、黑
enum Color { RED, BLACK };// 红黑树节点结构
template <typename K, typename V>
struct RBNode {K key; // 键V value; // 值Color color; // 颜色RBNode *left; // 左子节点RBNode *right; // 右子节点RBNode *parent; // 父节点RBNode(const K& k, const V& v) : key(k), value(v), color(RED), left(nullptr), right(nullptr), parent(nullptr) {}
};// 红黑树类
template <typename K, typename V>
class RedBlackTree {
private:RBNode<K, V>* root; // 根节点RBNode<K, V>* nil; // 哨兵节点(代替 NULL,颜色为 BLACK)int treeSize; // 树中节点数量// --- 辅助操作:左旋 ---void leftRotate(RBNode<K, V>* x) {RBNode<K, V>* y = x->right; // x 的右孩子x->right = y->left; // y 的左子树成为 x 的右子树if (y->left != nil) {y->left->parent = x;}y->parent = x->parent; // x 的父节点成为 y 的父节点if (x->parent == nil) { // x 是根节点时,y 成为新根root = y;} else if (x == x->parent->left) { // x 是父的左孩子x->parent->left = y;} else { // x 是父的右孩子x->parent->right = y;}y->left = x; // x 成为 y 的左孩子x->parent = y;}// --- 辅助操作:右旋 ---void rightRotate(RBNode<K, V>* y) {RBNode<K, V>* x = y->left; // y 的左孩子y->left = x->right; // x 的右子树成为 y 的左子树if (x->right != nil) {x->right->parent = y;}x->parent = y->parent; // y 的父节点成为 x 的父节点if (y->parent == nil) { // y 是根节点时,x 成为新根root = x;} else if (y == y->parent->left) { // y 是父的左孩子y->parent->left = x;} else { // y 是父的右孩子y->parent->right = x;}x->right = y; // y 成为 x 的右孩子y->parent = x;}// --- 插入后修复红黑树性质 ---void insertFixup(RBNode<K, V>* z) {// 当父节点为红色时,违反“红节点的子节点必为黑”的性质,需要修复while (z->parent->color == RED) {if (z->parent == z->parent->parent->left) { // 父节点是祖父的左孩子RBNode<K, V>* uncle = z->parent->parent->right; // 叔节点(祖父的右孩子)if (uncle->color == RED) { // 情况1:叔节点为红 → 变色即可z->parent->color = BLACK;uncle->color = BLACK;z->parent->parent->color = RED;z = z->parent->parent; // 祖父可能违反性质,继续向上修复} else { // 叔节点为黑if (z == z->parent->right) { // 情况2:z 是父的右孩子 → 先左旋z = z->parent;leftRotate(z);}// 情况3:z 是父的左孩子 → 父变黑色、祖父变红色,再右旋z->parent->color = BLACK;z->parent->parent->color = RED;rightRotate(z->parent->parent);}} else { // 对称情况:父节点是祖父的右孩子RBNode<K, V>* uncle = z->parent->parent->left; // 叔节点(祖父的左孩子)if (uncle->color == RED) { // 情况1:叔节点为红 → 变色z->parent->color = BLACK;uncle->color = BLACK;z->parent->parent->color = RED;z = z->parent->parent;} else { // 叔节点为黑if (z == z->parent->left) { // 情况2:z 是父的左孩子 → 先右旋z = z->parent;rightRotate(z);}// 情况3:z 是父的右孩子 → 父变黑色、祖父变红色,再左旋z->parent->color = BLACK;z->parent->parent->color = RED;leftRotate(z->parent->parent);}}}root->color = BLACK; // 确保根节点始终为黑色}// --- 查找节点(内部辅助)---RBNode<K, V>* searchNode(const K& key) const {RBNode<K, V>* curr = root;while (curr != nil) {if (key < curr->key) {curr = curr->left;} else if (key > curr->key) {curr = curr->right;} else {return curr; // 找到节点}}return nil; // 未找到}// --- 找子树中最小节点(用于删除时找后继)---RBNode<K, V>* minimum(RBNode<K, V>* node) const {while (node->left != nil) {node = node->left;}return node;}// --- 找子树中最大节点 ---RBNode<K, V>* maximum(RBNode<K, V>* node) const {while (node->right != nil) {node = node->right;}return node;}// --- 删除后修复红黑树性质 ---void deleteFixup(RBNode<K, V>* x) {// 当 x 为黑且非根时,可能违反“路径黑节点数相同”的性质,需要修复while (x != root && x->color == BLACK) {if (x == x->parent->left) { // x 是父的左孩子RBNode<K, V>* sibling = x->parent->right; // 兄弟节点if (sibling->color == RED) { // 情况1:兄弟是红 → 先变色+左旋,将兄弟转为黑sibling->color = BLACK;x->parent->color = RED;leftRotate(x->parent);sibling = x->parent->right;}if (sibling->left->color == BLACK && sibling->right->color == BLACK) { // 情况2:兄弟的子都是黑 → 兄弟变红,x 上移sibling->color = RED;x = x->parent;} else {if (sibling->right->color == BLACK) { // 情况3:兄弟右子是黑,左子是红 → 兄弟左旋,转为情况4sibling->left->color = BLACK;sibling->color = RED;rightRotate(sibling);sibling = x->parent->right;}// 情况4:兄弟右子是红 → 变色+左旋,修复完成sibling->color = x->parent->color;x->parent->color = BLACK;sibling->right->color = BLACK;leftRotate(x->parent);x = root; // 结束循环}} else { // 对称情况:x 是父的右孩子RBNode<K, V>* sibling = x->parent->left; // 兄弟节点if (sibling->color == RED) { // 情况1:兄弟是红 → 变色+右旋sibling->color = BLACK;x->parent->color = RED;rightRotate(x->parent);sibling = x->parent->left;}if (sibling->right->color == BLACK && sibling->left->color == BLACK) { // 情况2:兄弟的子都是黑 → 兄弟变红,x 上移sibling->color = RED;x = x->parent;} else {if (sibling->left->color == BLACK) { // 情况3:兄弟左子是黑,右子是红 → 兄弟右旋,转为情况4sibling->right->color = BLACK;sibling->color = RED;leftRotate(sibling);sibling = x->parent->left;}// 情况4:兄弟左子是红 → 变色+右旋,修复完成sibling->color = x->parent->color;x->parent->color = BLACK;sibling->left->color = BLACK;rightRotate(x->parent);x = root; // 结束循环}}}x->color = BLACK; // 确保 x 最终为黑色(若 x 是根,直接设为黑)}// --- 移植操作:用 v 替换 u(二叉搜索树通用操作)---void transplant(RBNode<K, V>* u, RBNode<K, V>* v) {if (u->parent == nil) {root = v;} else if (u == u->parent->left) {u->parent->left = v;} else {u->parent->right = v;}v->parent = u->parent;}// --- 删除节点(内部辅助,与二叉搜索树逻辑结合后修复)---void deleteNode(RBNode<K, V>* z) {RBNode<K, V>* y = z; // 记录要真正删除的节点RBNode<K, V>* x = nil; // 记录 y 的子节点(用于后续修复)Color y_original_color = y->color; // 记录 y 原始颜色(若为黑,删除后可能破坏性质)// 情况1:z 只有右孩子if (z->left == nil) {x = z->right;transplant(z, z->right);} // 情况2:z 只有左孩子else if (z->right == nil) {x = z->left;transplant(z, z->left);} // 情况3:z 有两个孩子 → 找后继(右子树最小节点)else {y = minimum(z->right);y_original_color = y->color;x = y->right;if (y->parent == z) { // 后继是 z 的直接右孩子x->parent = y;} else { // 后继不是 z 的直接右孩子 → 先移植后继的右子树transplant(y, y->right);y->right = z->right;y->right->parent = y;}// 用后继 y 替换 ztransplant(z, y);y->left = z->left;y->left->parent = y;y->color = z->color;}// 若删除的是黑色节点,可能破坏性质,需修复if (y_original_color == BLACK) {deleteFixup(x);}delete z; // 释放原节点内存treeSize--; // 减少节点计数}// --- 销毁树(析构时用)---void destroy(RBNode<K, V>* node) {if (node != nil) {destroy(node->left);destroy(node->right);delete node;}}// --- 前序遍历辅助函数 ---void preorderHelper(RBNode<K, V>* node, vector<pair<K, V>>& result) const {if (node != nil) {result.emplace_back(node->key, node->value);preorderHelper(node->left, result);preorderHelper(node->right, result);}}// --- 中序遍历辅助函数 ---void inorderHelper(RBNode<K, V>* node, vector<pair<K, V>>& result) const {if (node != nil) {inorderHelper(node->left, result);result.emplace_back(node->key, node->value);inorderHelper(node->right, result);}}// --- 后序遍历辅助函数 ---void postorderHelper(RBNode<K, V>* node, vector<pair<K, V>>& result) const {if (node != nil) {postorderHelper(node->left, result);postorderHelper(node->right, result);result.emplace_back(node->key, node->value);}}// --- 计算树高度辅助函数 ---int heightHelper(RBNode<K, V>* node) const {if (node == nil) {return 0;}int leftHeight = heightHelper(node->left);int rightHeight = heightHelper(node->right);return max(leftHeight, rightHeight) + 1;}// --- 打印树结构辅助函数 ---void printStructureHelper(RBNode<K, V>* node, string indent, bool last) const {if (node != nil) {cout << indent;if (last) {cout << "R----";indent += " ";} else {cout << "L----";indent += "| ";}string color = (node->color == RED) ? "RED" : "BLACK";cout << node->key << "(" << color << ")" << endl;printStructureHelper(node->left, indent, false);printStructureHelper(node->right, indent, true);}}// --- 范围查询辅助函数 ---void rangeQueryHelper(RBNode<K, V>* node, const K& minKey, const K& maxKey, vector<pair<K, V>>& result) const {if (node == nil) return;// 左子树可能有符合条件的节点if (node->key > minKey) {rangeQueryHelper(node->left, minKey, maxKey, result);}// 当前节点在范围内,加入结果if (node->key >= minKey && node->key <= maxKey) {result.emplace_back(node->key, node->value);}// 右子树可能有符合条件的节点if (node->key < maxKey) {rangeQueryHelper(node->right, minKey, maxKey, result);}}// --- 统计区间节点数辅助函数 ---int countRangeHelper(RBNode<K, V>* node, const K& minKey, const K& maxKey) const {if (node == nil) return 0;int count = 0;// 当前节点在范围内,计数+1if (node->key >= minKey && node->key <= maxKey) {count = 1;}// 左子树递归统计if (node->key > minKey) {count += countRangeHelper(node->left, minKey, maxKey);}// 右子树递归统计if (node->key < maxKey) {count += countRangeHelper(node->right, minKey, maxKey);}return count;}// --- 检查红黑树性质:性质1(颜色合法性)---bool checkColorProperty(RBNode<K, V>* node) const {if (node == nil) return true;if (node->color != RED && node->color != BLACK) return false;return checkColorProperty(node->left) && checkColorProperty(node->right);}// --- 检查红黑树性质:性质2(根为黑色)---bool checkRootProperty() const {return root->color == BLACK;}// --- 检查红黑树性质:性质4(红节点的子节点为黑)---bool checkRedParentProperty(RBNode<K, V>* node) const {if (node == nil) return true;if (node->color == RED) {if (node->left->color == RED || node->right->color == RED) {return false;}}return checkRedParentProperty(node->left) && checkRedParentProperty(node->right);}// --- 检查红黑树性质:性质5(每条路径黑节点数相同)---int blackHeight(RBNode<K, V>* node) const {if (node == nil) return 1; // 外部节点(nil)的黑高为1int leftBH = blackHeight(node->left);int rightBH = blackHeight(node->right);if (leftBH != rightBH) return -1; // 左右黑高不同,违反性质return leftBH + (node->color == BLACK ? 1 : 0);}bool checkBlackHeightProperty() const {return blackHeight(root) != -1;}public:// 构造函数:初始化哨兵和根RedBlackTree() {nil = new RBNode<K, V>(K(), V());nil->color = BLACK;root = nil;treeSize = 0;}// 析构函数:销毁所有节点~RedBlackTree() {destroy(root);delete nil;}// --- 对外插入接口 ---void insert(const K& key, const V& value) {RBNode<K, V>* z = new RBNode<K, V>(key, value);z->left = z->right = z->parent = nil; // 新节点的子、父初始指向哨兵RBNode<K, V>* parent = nil;RBNode<K, V>* curr = root;// 找插入位置(同二叉搜索树)while (curr != nil) {parent = curr;if (z->key < curr->key) {curr = curr->left;} else {curr = curr->right;}}z->parent = parent;if (parent == nil) { // 树为空,新节点为根root = z;} else if (z->key < parent->key) { // 插在父的左子树parent->left = z;} else { // 插在父的右子树parent->right = z;}insertFixup(z); // 修复红黑树性质treeSize++; // 增加节点计数}// --- 对外查找接口 ---bool search(const K& key, V& value) const {RBNode<K, V>* node = searchNode(key);if (node != nil) {value = node->value;return true;}return false;}// --- 对外删除接口 ---bool remove(const K& key) {RBNode<K, V>* node = searchNode(key);if (node == nil) {return false; // 未找到节点}deleteNode(node);return true;}// --- 更新节点值 ---bool update(const K& key, const V& newValue) {RBNode<K, V>* node = searchNode(key);if (node != nil) {node->value = newValue;return true;}return false;}// --- 前序遍历 ---vector<pair<K, V>> preorder() const {vector<pair<K, V>> result;preorderHelper(root, result);return result;}// --- 中序遍历 ---vector<pair<K, V>> inorder() const {vector<pair<K, V>> result;inorderHelper(root, result);return result;}// --- 后序遍历 ---vector<pair<K, V>> postorder() const {vector<pair<K, V>> result;postorderHelper(root, result);return result;}// --- 层序遍历 ---vector<pair<K, V>> levelorder() const {vector<pair<K, V>> result;if (root == nil) return result;queue<RBNode<K, V>*> q;q.push(root);while (!q.empty()) {RBNode<K, V>* node = q.front();q.pop();result.emplace_back(node->key, node->value);if (node->left != nil)q.push(node->left);if (node->right != nil)q.push(node->right);}return result;}// --- 获取树的大小 ---int size() const {return treeSize;}// --- 获取树的高度 ---int height() const {return heightHelper(root);}// --- 检查树是否为空 ---bool isEmpty() const {return root == nil;}// --- 清空树 ---void clear() {destroy(root);root = nil;treeSize = 0;}// --- 查找最小值 ---bool findMin(K& key, V& value) const {if (isEmpty()) return false;RBNode<K, V>* node = minimum(root);key = node->key;value = node->value;return true;}// --- 查找最大值 ---bool findMax(K& key, V& value) const {if (isEmpty()) return false;RBNode<K, V>* node = maximum(root);key = node->key;value = node->value;return true;}// --- 查找前驱节点(小于当前键的最大键)---bool findPredecessor(const K& key, K& predKey, V& predValue) const {RBNode<K, V>* node = searchNode(key);if (node == nil) return false;// 左子树非空:前驱是左子树的最大值if (node->left != nil) {RBNode<K, V>* pred = maximum(node->left);predKey = pred->key;predValue = pred->value;return true;}// 左子树为空:向上找第一个有右子树的祖先RBNode<K, V>* pred = node->parent;while (pred != nil && node == pred->left) {node = pred;pred = pred->parent;}if (pred != nil) {predKey = pred->key;predValue = pred->value;return true;}return false; // 无前列}// --- 查找后继节点(大于当前键的最小键)---bool findSuccessor(const K& key, K& succKey, V& succValue) const {RBNode<K, V>* node = searchNode(key);if (node == nil) return false;// 右子树非空:后继是右子树的最小值if (node->right != nil) {RBNode<K, V>* succ = minimum(node->right);succKey = succ->key;succValue = succ->value;return true;}// 右子树为空:向上找第一个有左子树的祖先RBNode<K, V>* succ = node->parent;while (succ != nil && node == succ->right) {node = succ;succ = succ->parent;}if (succ != nil) {succKey = succ->key;succValue = succ->value;return true;}return false; // 无后继}// --- 打印树结构 ---void printStructure() const {if (isEmpty()) {cout << "Tree is empty" << endl;return;}printStructureHelper(root, "", true);}// --- 范围查询:返回键在 [minKey, maxKey] 之间的键值对 ---vector<pair<K, V>> rangeQuery(const K& minKey, const K& maxKey) const {vector<pair<K, V>> result;rangeQueryHelper(root, minKey, maxKey, result);return result;}// --- 统计区间节点数:返回键在 [minKey, maxKey] 之间的节点数量 ---int countRange(const K& minKey, const K& maxKey) const {return countRangeHelper(root, minKey, maxKey);}// --- 检查红黑树是否满足所有性质(性质1-5)---bool checkProperties() const {bool valid = true;if (!checkColorProperty(root)) {cout << "违反性质1:节点颜色不是红色或黑色" << endl;valid = false;}if (!checkRootProperty()) {cout << "违反性质2:根节点不是黑色" << endl;valid = false;}if (!checkRedParentProperty(root)) {cout << "违反性质4:存在红色节点的子节点为红色" << endl;valid = false;}if (!checkBlackHeightProperty()) {cout << "违反性质5:从根到叶的路径黑节点数不同" << endl;valid = false;}if (valid) cout << "红黑树所有性质均满足" << endl;return valid;}
};int main() {// 测试<int, string>类型的红黑树RedBlackTree<int, string> rbt;cout << "=== 测试插入操作 ===" << endl;rbt.insert(10, "Ten");rbt.insert(20, "Twenty");rbt.insert(5, "Five");rbt.insert(15, "Fifteen");rbt.insert(30, "Thirty");rbt.insert(25, "Twenty-five");rbt.insert(35, "Thirty-five");cout << "树的大小: " << rbt.size() << endl;cout << "树的高度: " << rbt.height() << endl;cout << "\n=== 测试树结构 ===" << endl;rbt.printStructure();cout << "\n=== 测试查找操作 ===" << endl;string val;if (rbt.search(10, val)) {cout << "找到键 10: 值 = " << val << endl;} else {cout << "未找到键 10!" << endl;}if (rbt.search(15, val)) {cout << "找到键 15: 值 = " << val << endl;}cout << "\n=== 测试更新操作 ===" << endl;if (rbt.update(10, "Ten Updated")) {if (rbt.search(10, val)) {cout << "更新后键 10 的值: " << val << endl;}}cout << "\n=== 测试遍历操作 ===" << endl;auto preorder = rbt.preorder();cout << "前序遍历: ";for (const auto& p : preorder) cout << p.first << " ";cout << endl;auto inorder = rbt.inorder();cout << "中序遍历: ";for (const auto& p : inorder) cout << p.first << " ";cout << endl;auto postorder = rbt.postorder();cout << "后序遍历: ";for (const auto& p : postorder) cout << p.first << " ";cout << endl;auto levelorder = rbt.levelorder();cout << "层序遍历: ";for (const auto& p : levelorder) cout << p.first << " ";cout << endl;cout << "\n=== 测试最大最小值 ===" << endl;int minKey, maxKey;string minVal, maxVal;if (rbt.findMin(minKey, minVal)) {cout << "最小值: " << minKey << " (" << minVal << ")" << endl;}if (rbt.findMax(maxKey, maxVal)) {cout << "最大值: " << maxKey << " (" << maxVal << ")" << endl;}cout << "\n=== 测试前驱后继 ===" << endl;int predKey, succKey;string predVal, succVal;if (rbt.findPredecessor(20, predKey, predVal)) {cout << "20 的前驱: " << predKey << " (" << predVal << ")" << endl;}if (rbt.findSuccessor(20, succKey, succVal)) {cout << "20 的后继: " << succKey << " (" << succVal << ")" << endl;}cout << "\n=== 测试删除操作 ===" << endl;if (rbt.remove(10)) {cout << "已删除键 10" << endl;if (!rbt.search(10, val)) {cout << "确认键 10 已删除" << endl;}}cout << "删除后树的大小: " << rbt.size() << endl;cout << "删除后树结构: " << endl;rbt.printStructure();cout << "\n=== 测试范围查询 ===" << endl;auto range = rbt.rangeQuery(15, 30);cout << "键在 15 到 30 之间的节点: " << endl;for (const auto& p : range) {cout << "键: " << p.first << ", 值: " << p.second << endl;}cout << "该区间内节点数量: " << rbt.countRange(15, 30) << endl;cout << "\n=== 测试红黑树性质检查 ===" << endl;rbt.checkProperties();cout << "\n=== 测试清空操作 ===" << endl;rbt.clear();cout << "清空后树的大小: " << rbt.size() << endl;cout << "树是否为空: " << (rbt.isEmpty() ? "是" : "否") << endl;return 0;
}"""self.code_text.config(state=tk.NORMAL)self.code_text.delete(1.0, tk.END)self.code_text.insert(tk.END, cpp_code)self.code_text.config(state=tk.DISABLED)def update_status(self, message):self.status_var.set(message)self.root.update_idletasks()def update_delay(self, event=None):delay = int(self.delay_scale.get())self.rbt.set_delay(delay)def update_tree_layout(self, node=None, x=400, y=50, level_height=80, level_width=150):if node is None:node = self.rbt.rootif node == self.rbt.nil:return# 设置当前节点位置node.x = xnode.y = y# 初始化动画位置node.anim_x = xnode.anim_y = y# 递归设置左子树if node.left != self.rbt.nil:self.update_tree_layout(node.left, x - level_width, y + level_height,level_height, max(30, level_width // 2)) # 确保level_width不会太小# 递归设置右子树if node.right != self.rbt.nil:self.update_tree_layout(node.right, x + level_width, y + level_height,level_height, max(30, level_width // 2))def draw_tree(self):# 清空画布self.canvas.delete("all")# 绘制树self._draw_node(self.rbt.root)# 更新滚动区域self.canvas.configure(scrollregion=self.canvas.bbox("all"))def _draw_node(self, node):if node == self.rbt.nil:return# 绘制连接线if node.left != self.rbt.nil:self.canvas.create_line(node.anim_x, node.anim_y + 20,node.left.anim_x, node.left.anim_y - 20,width=2, fill="#666666")self._draw_node(node.left)if node.right != self.rbt.nil:self.canvas.create_line(node.anim_x, node.anim_y + 20,node.right.anim_x, node.right.anim_y - 20,width=2, fill="#666666")self._draw_node(node.right)# 绘制节点color = "red" if node.color == RED else "black"text_color = "white" if color == "black" else "black"# 检查是否为高亮节点highlight_type = self.highlighted_nodes.get(node, None)if highlight_type:highlight_colors = {"insert": "#00ff00", # 绿色"delete": "#ff6666", # 浅红"search": "#ffff99", # 浅黄"found": "#99ff99", # 浅绿"current": "#99ffff", # 浅青"parent": "#ffcc99", # 浅橙"grandparent": "#cc99ff", # 浅紫"uncle": "#99ccff", # 浅蓝"sibling": "#ff99cc", # 浅粉"rotate": "#99ffcc", # 浅青绿"transplant": "#ffccff", # 浅洋红"minimum": "#ffff00", # 黄色"maximum": "#00ffff", # 青色"predecessor": "#00cc00", # 深绿"successor": "#0000cc", # 深蓝"update": "#ff9900", # 橙色"error": "#ff0000" # 红色}# 先绘制高亮背景self.canvas.create_oval(node.anim_x - 25, node.anim_y - 25,node.anim_x + 25, node.anim_y + 25,fill=highlight_colors[highlight_type], outline="black", width=2)# 绘制节点本身self.canvas.create_oval(node.anim_x - 20, node.anim_y - 20,node.anim_x + 20, node.anim_y + 20,fill=color, outline="black", width=2)self.canvas.create_text(node.anim_x, node.anim_y, text=str(node.key),fill=text_color, font=("Arial", 12, "bold"))# 显示节点值(如果不是默认值)if node.value != str(node.key):self.canvas.create_text(node.anim_x, node.anim_y + 30, text=f"值: {node.value}",fill="#333333", font=("Arial", 8))def highlight_node(self, node, highlight_type):if node != self.rbt.nil:self.highlighted_nodes[node] = highlight_typedef clear_highlights(self):self.highlighted_nodes.clear()self.highlighted_edges = []def animate_rotation(self, node1, node2, rotation_type):# 记录初始位置start_x1, start_y1 = node1.x, node1.ystart_x2, start_y2 = node2.x, node2.y# 动画步数steps = 10for i in range(steps + 1):# 计算当前步骤的位置progress = i / stepsif rotation_type == "left":# 左旋动画:node1向左下旋转,node2向右上旋转node1.anim_x = start_x1 + (node1.x - start_x1) * progressnode1.anim_y = start_y1 + (node1.y - start_y1) * progressnode2.anim_x = start_x2 + (node2.x - start_x2) * progressnode2.anim_y = start_y2 + (node2.y - start_y2) * progresselse: # right# 右旋动画:node1向右下旋转,node2向左上旋转node1.anim_x = start_x1 + (node1.x - start_x1) * progressnode1.anim_y = start_y1 + (node1.y - start_y1) * progressnode2.anim_x = start_x2 + (node2.x - start_x2) * progressnode2.anim_y = start_y2 + (node2.y - start_y2) * progress# 重绘树self.draw_tree()self.root.update()time.sleep(self.rbt.animation_step / 1000)# 确保最终位置正确node1.anim_x, node1.anim_y = node1.x, node1.ynode2.anim_x, node2.anim_y = node2.x, node2.ydef animate_transplant(self, u, v):if v == self.rbt.nil:return# 动画步数steps = 10start_x, start_y = v.x, v.yfor i in range(steps + 1):# 计算当前步骤的位置(从原位置移动到u的位置)progress = i / stepsv.anim_x = start_x + (u.x - start_x) * progressv.anim_y = start_y + (u.y - start_y) * progress# 重绘树self.draw_tree()self.root.update()time.sleep(self.rbt.animation_step / 1000)# 确保最终位置正确v.anim_x, v.anim_y = v.x, v.y# 添加update方法,用于刷新可视化界面def update(self):"""刷新红黑树的可视化显示"""self.draw_tree()self.root.update_idletasks()self.root.update()def insert_node(self):try:key = int(self.key_entry.get())self.key_entry.delete(0, tk.END)# 在新线程中执行插入操作,避免UI冻结def insert_task():self.rbt.insert(key)self.update_tree_layout()self.draw_tree()threading.Thread(target=insert_task, daemon=True).start()except ValueError:messagebox.showerror("输入错误", "请输入有效的整数键值")def delete_node(self):try:key = int(self.key_entry.get())self.key_entry.delete(0, tk.END)# 在新线程中执行删除操作def delete_task():success = self.rbt.delete(key)if not success:self.root.after(0, lambda: messagebox.showerror("删除失败", f"键 {key} 不存在"))self.update_tree_layout()self.draw_tree()threading.Thread(target=delete_task, daemon=True).start()except ValueError:messagebox.showerror("输入错误", "请输入有效的整数键值")def search_node(self):try:key = int(self.key_entry.get())self.key_entry.delete(0, tk.END)# 在新线程中执行查找操作def search_task():value = self.rbt.search(key)if value is not None:self.root.after(0, lambda: messagebox.showinfo("查找结果", f"找到键 {key},值为 {value}"))else:self.root.after(0, lambda: messagebox.showerror("查找失败", f"键 {key} 不存在"))self.update_tree_layout()self.draw_tree()threading.Thread(target=search_task, daemon=True).start()except ValueError:messagebox.showerror("输入错误", "请输入有效的整数键值")def update_node(self):try:key = int(self.key_entry.get())self.key_entry.delete(0, tk.END)new_value = simpledialog.askstring("更新节点值", f"请输入键 {key} 的新值:")if new_value is None: # 用户取消输入return# 在新线程中执行更新操作def update_task():success = self.rbt.update(key, new_value)if not success:self.root.after(0, lambda: messagebox.showerror("更新失败", f"键 {key} 不存在"))self.update_tree_layout()self.draw_tree()threading.Thread(target=update_task, daemon=True).start()except ValueError:messagebox.showerror("输入错误", "请输入有效的整数键值")def find_predecessor(self):try:key = int(self.key_entry.get())self.key_entry.delete(0, tk.END)# 在新线程中执行查找前驱操作def pred_task():pred = self.rbt.find_predecessor(key)if pred is not None:self.root.after(0, lambda: messagebox.showinfo("前驱节点",f"{key} 的前驱节点是 {pred.key} (值: {pred.value})"))else:self.root.after(0, lambda: messagebox.showinfo("前驱节点", f"{key} 没有前驱节点或节点不存在"))self.update_tree_layout()self.draw_tree()threading.Thread(target=pred_task, daemon=True).start()except ValueError:messagebox.showerror("输入错误", "请输入有效的整数键值")def find_successor(self):try:key = int(self.key_entry.get())self.key_entry.delete(0, tk.END)# 在新线程中执行查找后继操作def succ_task():succ = self.rbt.find_successor(key)if succ is not None:self.root.after(0, lambda: messagebox.showinfo("后继节点",f"{key} 的后继节点是 {succ.key} (值: {succ.value})"))else:self.root.after(0, lambda: messagebox.showinfo("后继节点", f"{key} 没有后继节点或节点不存在"))self.update_tree_layout()self.draw_tree()threading.Thread(target=succ_task, daemon=True).start()except ValueError:messagebox.showerror("输入错误", "请输入有效的整数键值")def find_min(self):# 在新线程中执行查找最小值操作def min_task():min_node = self.rbt.find_min()if min_node is not None:self.root.after(0, lambda: messagebox.showinfo("最小值",f"树中的最小值是 {min_node.key} (值: {min_node.value})"))else:self.root.after(0, lambda: messagebox.showinfo("最小值", "树为空,没有最小值"))self.update_tree_layout()self.draw_tree()threading.Thread(target=min_task, daemon=True).start()def find_max(self):# 在新线程中执行查找最大值操作def max_task():max_node = self.rbt.find_max()if max_node is not None:self.root.after(0, lambda: messagebox.showinfo("最大值",f"树中的最大值是 {max_node.key} (值: {max_node.value})"))else:self.root.after(0, lambda: messagebox.showinfo("最大值", "树为空,没有最大值"))self.update_tree_layout()self.draw_tree()threading.Thread(target=max_task, daemon=True).start()def range_query(self):try:min_key = int(self.min_range_entry.get())max_key = int(self.max_range_entry.get())self.min_range_entry.delete(0, tk.END)self.max_range_entry.delete(0, tk.END)if min_key > max_key:min_key, max_key = max_key, min_key# 在新线程中执行范围查询操作def range_task():result = self.rbt.range_query(min_key, max_key)if result:msg = f"键在 [{min_key}, {max_key}] 范围内的节点:\n"for key, value in result:msg += f"键: {key}, 值: {value}\n"self.root.after(0, lambda: messagebox.showinfo("范围查询结果", msg))else:self.root.after(0, lambda: messagebox.showinfo("范围查询结果",f"在 [{min_key}, {max_key}] 范围内没有找到节点"))self.update_tree_layout()self.draw_tree()threading.Thread(target=range_task, daemon=True).start()except ValueError:messagebox.showerror("输入错误", "请输入有效的整数键值")def count_range(self):try:min_key = int(self.min_range_entry.get())max_key = int(self.max_range_entry.get())self.min_range_entry.delete(0, tk.END)self.max_range_entry.delete(0, tk.END)if min_key > max_key:min_key, max_key = max_key, min_key# 在新线程中执行区间计数操作def count_task():count = self.rbt.count_range(min_key, max_key)self.root.after(0, lambda: messagebox.showinfo("区间计数结果",f"在 [{min_key}, {max_key}] 范围内共有 {count} 个节点"))self.update_tree_layout()self.draw_tree()threading.Thread(target=count_task, daemon=True).start()except ValueError:messagebox.showerror("输入错误", "请输入有效的整数键值")def check_properties(self):# 在新线程中执行检查性质操作def check_task():valid = self.rbt.check_properties()if valid:self.root.after(0, lambda: messagebox.showinfo("红黑树性质检查", "红黑树满足所有性质!"))else:self.root.after(0, lambda: messagebox.showerror("红黑树性质检查", "红黑树违反了某些性质!"))self.update_tree_layout()self.draw_tree()threading.Thread(target=check_task, daemon=True).start()def clear_tree(self):# 清空树def clear_task():self.rbt.clear()self.update_tree_layout()self.draw_tree()threading.Thread(target=clear_task, daemon=True).start()def traverse(self, traverse_type):# 在新线程中执行遍历操作def traverse_task():self.update_status(f"执行{traverse_type}遍历...")self.clear_highlights()self.update()if traverse_type == "preorder":nodes = self.rbt.preorder()self.update_status("执行前序遍历...")elif traverse_type == "inorder":nodes = self.rbt.inorder()self.update_status("执行中序遍历...")elif traverse_type == "postorder":nodes = self.rbt.postorder()self.update_status("执行后序遍历...")else: # levelordernodes = self.rbt.levelorder()self.update_status("执行层序遍历...")# 动画展示遍历过程traversal_result = []for key, value in nodes:node = self.rbt.search_node(key)self.clear_highlights()self.highlight_node(node, "found")self.update_status(f"{traverse_type}遍历: 访问节点 {key}")self.draw_tree()self.update()time.sleep(self.rbt.delay / 1000)traversal_result.append(str(key))# 显示遍历结果result_str = ", ".join(traversal_result)self.root.after(0, lambda: messagebox.showinfo(f"{traverse_type}遍历结果",f"{traverse_type}遍历顺序: {result_str}"))self.clear_highlights()self.update_status(f"完成{traverse_type}遍历")self.draw_tree()self.update()threading.Thread(target=traverse_task, daemon=True).start()def run_example(self):# 运行示例操作,对应C++代码中的main函数测试def example_task():# 清空现有树self.update_status("运行示例: 清空现有树")self.update()time.sleep(self.rbt.delay / 1000)nodes = self.rbt.inorder()for key, _ in nodes:self.rbt.delete(key)self.update_tree_layout()self.draw_tree()time.sleep(self.rbt.delay / 2000)# 插入示例节点self.update_status("运行示例: 开始插入节点")self.update()time.sleep(self.rbt.delay / 1000)example_keys = [10, 20, 5, 15, 30, 25, 35]example_values = ["Ten", "Twenty", "Five", "Fifteen", "Thirty", "Twenty-five", "Thirty-five"]for i in range(len(example_keys)):self.rbt.insert(example_keys[i], example_values[i])self.update_tree_layout()self.draw_tree()time.sleep(self.rbt.delay / 1000)# 显示树信息self.update_status(f"示例: 树的大小 = {self.rbt.size()}, 高度 = {self.rbt.height()}")self.update()time.sleep(self.rbt.delay * 1.5 / 1000)# 测试查找self.update_status("示例: 测试查找操作")self.update()time.sleep(self.rbt.delay / 1000)self.rbt.search(10)time.sleep(self.rbt.delay / 1000)self.rbt.search(15)time.sleep(self.rbt.delay / 1000)# 测试更新self.update_status("示例: 测试更新操作")self.update()time.sleep(self.rbt.delay / 1000)self.rbt.update(10, "Ten Updated")time.sleep(self.rbt.delay * 1.5 / 1000)# 测试遍历self.update_status("示例: 测试中序遍历")self.update()time.sleep(self.rbt.delay / 1000)inorder = self.rbt.inorder()inorder_keys = [str(key) for key, _ in inorder]self.update_status(f"中序遍历结果: {', '.join(inorder_keys)}")self.update()time.sleep(self.rbt.delay * 1.5 / 1000)# 测试最大最小值self.update_status("示例: 测试最大最小值")self.update()time.sleep(self.rbt.delay / 1000)self.rbt.find_min()time.sleep(self.rbt.delay / 1000)self.rbt.find_max()time.sleep(self.rbt.delay / 1000)# 测试前驱后继self.update_status("示例: 测试前驱后继")self.update()time.sleep(self.rbt.delay / 1000)self.rbt.find_predecessor(20)time.sleep(self.rbt.delay / 1000)self.rbt.find_successor(20)time.sleep(self.rbt.delay / 1000)# 测试删除self.update_status("示例: 测试删除操作")self.update()time.sleep(self.rbt.delay / 1000)self.rbt.delete(10)time.sleep(self.rbt.delay * 1.5 / 1000)# 测试范围查询self.update_status("示例: 测试范围查询")self.update()time.sleep(self.rbt.delay / 1000)self.rbt.range_query(15, 30)time.sleep(self.rbt.delay / 1000)self.rbt.count_range(15, 30)time.sleep(self.rbt.delay * 1.5 / 1000)# 测试红黑树性质检查self.update_status("示例: 检查红黑树性质")self.update()time.sleep(self.rbt.delay / 1000)self.rbt.check_properties()time.sleep(self.rbt.delay * 1.5 / 1000)# 完成示例self.update_status("示例运行完成")self.update()threading.Thread(target=example_task, daemon=True).start()def main():root = tk.Tk()root.title("红黑树可视化工具")root.geometry("1600x900")# 创建红黑树实例rbt = RedBlackTree()# 创建可视化器visualizer = RedBlackTreeVisualizer(root, rbt)root.mainloop()if __name__ == "__main__":main()
六、程序运行的部分截图展示
七、总结
本文介绍了一个功能完整的红黑树可视化工具,通过Python实现并结合Tkinter构建GUI界面。该工具完整实现了红黑树的核心操作(插入、删除、查找、平衡修复等),并通过动画效果直观展示各个操作步骤。主要特点包括:
- 可视化功能:支持节点高亮、旋转动画、查找路径展示等视觉效果,帮助理解红黑树的自平衡机制;
- 交互操作:提供插入、删除、查找、更新等基本操作按钮,以及前驱/后继查询、范围查询等高级功能;
- 教学辅助:内置C++代码对照区、示例演示功能和红黑树性质检查工具,适合数据结构教学;
- 性能优化:采用多线程处理耗时操作,避免界面冻结,保证动画流畅性。
该工具既可作为学习红黑树的辅助材料,也可作为验证红黑树算法的可视化平台。