pygraphviz的安装与红黑树可视化

article/2025/4/24 19:05:56

大家好,我是小小明,今天我将带大家安装pygraphviz,并通过它对红黑树这种数据结构进行可视化。

pygraphviz的安装与红黑树的可视化

安装graphviz

下载地址:http://www.graphviz.org/download/

windows平台下可以选择:

  • Stable 2.38 Windows install packages

安装完成后将bin目录添加到环境变量中。

验证安装:

C:\Users\Administrator>dot -version
dot - graphviz version 2.38.0 (20140413.2041)
libdir = "D:\Program Files (x86)\Graphviz2.38\bin"
Activated plugin library: gvplugin_dot_layout.dll
Using layout: dot:dot_layout
Activated plugin library: gvplugin_core.dll
Using render: dot:core
Using device: dot:dot:core
The plugin configuration file:D:\Program Files (x86)\Graphviz2.38\bin\config6was successfully loaded.render      :  cairo dot fig gd gdiplus map pic pov ps svg tk vml vrml xdotlayout      :  circo dot fdp neato nop nop1 nop2 osage patchwork sfdp twopitextlayout  :  textlayoutdevice      :  bmp canon cmap cmapx cmapx_np dot emf emfplus eps fig gd gd2 gif gv ima
p imap_np ismap jpe jpeg jpg metafile pdf pic plain plain-ext png pov ps ps2 svg svgz tif
tiff tk vml vmlz vrml wbmp xdot xdot1.2 xdot1.4loadimage   :  (lib) bmp eps gd gd2 gif jpe jpeg jpg png ps svg

出现类似上面的版本信息,表示安装成功。

安装pygraphviz

下载地址:https://github.com/CristiFati/Prebuilt-Binaries/tree/master/PyGraphviz

从上面地址下载指定python版本的pygraphviz,我下载的是64位系统python3.7版本:

https://github.com/CristiFati/Prebuilt-Binaries/raw/master/PyGraphviz/pygraphviz-1.5-cp37-cp37m-win_amd64.whl

然后在下载目录中运行以下命令即可安装成功:

pip install pygraphviz-1.5-cp37-cp37m-win_amd64.whl

红黑树的概念

平衡二叉查找树中“平衡”的意思,其实就是让整棵树左右看起来比较“对称”、比较“平衡”,不要出现左子树很高、右子树很矮的情况。这样就能让整棵树的高度相对来说低一些,相应的插入、删除、查找等操作的效率高一些。

平衡二叉查找树的初衷,是为了解决二叉查找树因为动态更新导致的性能退化问题。

红黑树(Red-Black Tree,简称 R-B Tree)是最常用的平衡二叉查找树,除此之外平衡二叉查找树还有Splay Tree(伸展树)、Treap(树堆)等。它是一种不严格的平衡二叉查找树,

红黑树中的节点,一类被标记为黑色,一类被标记为红色。

红黑树满足:

  • 根节点是黑色的;
  • 每个叶子节点都是黑色的空节点(NIL),叶子节点不存储数据;
  • 任何相邻的节点都不能同时为红色,红色节点是被黑色节点隔开的;
  • 每个节点,从该节点到达其可达叶子节点的所有路径都包含相同数目的黑色节点;

如何比较轻松学会红黑树:

第一点,把红黑树的平衡调整的过程比作魔方复原,不要过于深究这个算法的正确性。只需要明白,只要按照固定的操作步骤,保持插入、删除的过程,不破坏平衡树的定义就行了。

第二点,找准关注节点,不要搞丢、搞错关注节点。因为每种操作规则,都是基于关注节点来做的,只有弄对了关注节点,才能对应到正确的操作规则中。在迭代的调整过程中,关注节点在不停地改变,所以,这个过程一定要注意,不要弄丢了关注节点。

第三点,插入操作的平衡调整比较简单,但是删除操作就比较复杂。针对删除操作,我们有两次调整,第一次是针对要删除的节点做初步调整,让调整后的红黑树继续满足第四条定义,“每个节点到可达叶子节点的路径都包含相同个数的黑色节点”。但是这个时候,第三条定义就不满足了,有可能会存在两个红色节点相邻的情况。第二次调整就是解决这个问题,让红黑树不存在相邻的红色节点。

红黑树的实现代码(含可视化)

# -*- coding: UTF-8 -*-
from collections import deque
from typing import Optionalimport pygraphviz as pgvclass TreeNode:def __init__(self, data=None, color=None):self.data = dataassert color in ['r', 'b']self.color = 'red' if color == 'r' else 'black'self.left = Noneself.right = Noneself.parent = Nonedef is_black(self) -> bool:return self.color == 'black'def is_red(self) -> bool:return self.color == 'red'def set_black(self):self.color = 'black'def set_red(self):self.color = 'red'class RedBlackTree:def __init__(self, val_list=None):self.tree: Optional[TreeNode] = Noneself.black_leaf = TreeNode(color='b')  # 共用的黑色叶子节点if val_list is None:val_list = []for n in val_list:self.insert(n)def find(self, data) -> Optional[TreeNode]:if self.tree is None:return Nonep = self.treewhile p != self.black_leaf:if data < p.data:p = p.leftelif data > p.data:p = p.rightelse:return preturn Nonedef insert(self, data):new_node = TreeNode(data, 'r')  # 新插入的节点为红色# 根节点if self.tree is None:self.tree = new_nodeelse:p = self.tree  # 根节点while p != self.black_leaf:  # 黑色叶子节点pp = p  # pp表示插入点的父节点if data < p.data:p = p.leftelif data > p.data:p = p.rightelse:# raise KeyError('val:{} already exists' % data)  # 该值已存在,插入失败returnif data < pp.data:pp.left = new_nodeelse:pp.right = new_nodenew_node.parent = ppnew_node.left = new_node.right = self.black_leaf# 插入后调整self._insert_fixup(new_node)def _insert_fixup(self, node):n: TreeNode = node  # 关注节点while n != self.tree and n.parent.is_red():# 父p 叔u 祖父gp = self.parent(n)u = self.bro(p)g = self.parent(p)if u.is_red():  # case 1 叔叔节点是红色p.set_black()  # 父节点设置成黑色u.set_black()  # 叔叔节点设置成黑色g.set_red()  # 祖父节点设置成红色n = g  # 关注节点变成祖父节点continue# 往下走,说明叔叔节点是黑色if p == g.left:  # 父节点为祖父节点的左子结点if n == p.right:  # case 2 关注节点是其父节点的右子节点self.rotate_l(p)  # 围绕关注节点的父节点左旋n, p = p, n  # 左旋后指针交换,关注节点设置为关注节点的父节点# case 3 关注节点是其父节点的左子节点p.set_black()  # 关注节点的父节点设置为黑色g.set_red()  # 关注节点的祖父节点设置为红色self.rotate_r(g)  # 围绕关注节点的祖父节点右旋else:  # 父节点为祖父节点的右子结点if n == p.left:  # case 2 关注节点是其父节点的左子节点self.rotate_r(p)  # 围绕关注节点的父节点右旋n, p = p, n  # 右旋后指针交换,关注节点设置为关注节点的父节点# case 3 关注节点是其父节点的右子节点p.set_black()  # 关注节点的父节点设置为黑色g.set_red()  # 关注节点的祖父节点设置为红色self.rotate_l(g)  # 围绕关注节点的祖父节点左旋# 根节点强制置黑,有两种情况根节点是红色:# 1. 新插入时是红色# 2. 经过case 1调整过后变红色self.tree.color = 'black'def delete(self, data):n: TreeNode = self.find(data)if n is None:return# n的子节点个数等于2if self.children_count(n) == 2:# 寻找n的后继ss = n.rightwhile s.left != self.black_leaf:s = s.leftn.data = s.data# 将删除n转化为删除sn = s# n的子节点个数小于2if n.left == self.black_leaf:c = n.rightelse:c = n.leftself._transplant(n, c)# 删除的节点是黑色,需要调整if n.is_black():self._delete_fixup(c)returndef _delete_fixup(self, node):n = nodewhile n != self.tree and n.is_black():p = self.parent(n)b = self.bro(n)# 左右节点对称if p.left == n:if not b.is_black():b.set_black()  # case 1p.set_red()  # case 1self.rotate_l(p)  # case 1# new bro after rotateb = self.bro(n)  # case 1if b.left.is_black() and b.right.is_black():b.set_red()  # case 2n = p  # case 2else:if b.right.is_black():b.left.set_black()  # case 3b.set_red()  # case 3self.rotate_r(b)  # case 3# new bro after rotateb = self.bro(n)  # case 3# 注意,因为p可能是红或黑,所以不能直接赋值颜色,只能copyb.color = p.color  # case 4p.set_black()  # case 4b.right.set_black()  # case 4self.rotate_l(p)  # case 4# trick, 调整结束跳出whilen = self.tree  # case 4else:if not b.is_black():b.set_black()  # case 1p.set_red()  # case 1self.rotate_r(p)  # case 1# new bro after rotateb = self.bro(n)  # case 1if b.left.is_black() and b.right.is_black():b.set_red()  # case 2n = p  # case 2else:if b.left.is_black():b.right.set_black()  # case 3b.set_red()  # case 3self.rotate_l(b)  # case 3# new bro after rotateb = self.bro(n)  # case 3# 注意,因为p可能是红或黑,所以不能直接赋值颜色,只能copyb.color = p.color  # case 4p.set_black()  # case 4b.left.set_black()  # case 4self.rotate_r(p)  # case 4# trick, 调整结束跳出whilen = self.tree  # case 4# 将n设为黑色,从上面while循环跳出,情况有两种# 1. n是根节点,直接无视附加的黑色# 2. n是红色的节点,则染黑n.set_black()def _transplant(self, n1, n2):"""节点移植, n2 -> n1:param n1: 原节点:param n2: 移植节点:return:"""if n1 == self.tree:if n2 != self.black_leaf:self.tree = n2n2.parent = Noneelse:self.tree = None  # 只有删除根节点时会进来else:p = self.parent(n1)if p.left == n1:p.left = n2else:p.right = n2n2.parent = pdef rotate_l(self, node):if node is None:returnif node.right is self.black_leaf:returnp = self.parent(node)x = nodey = node.right# node为根节点时,p为None,旋转后要更新根节点指向if p is not None:if x == p.left:p.left = yelse:p.right = yelse:self.tree = yx.parent, y.parent = y, pif y.left != self.black_leaf:y.left.parent = xx.right, y.left = y.left, xdef rotate_r(self, node):if node is None:returnif node.left is self.black_leaf:returnp = self.parent(node)x = nodey = node.left# 同左旋if p is not None:if x == p.left:p.left = yelse:p.right = yelse:self.tree = yx.parent, y.parent = y, pif y.right is not None:y.right.parent = xx.left, y.right = y.right, x@staticmethoddef bro(node):"""获取兄弟节点"""if node is None or node.parent is None:return Noneelse:p = node.parentif node == p.left:return p.rightelse:return p.left@staticmethoddef parent(node):"""获取父节点"""if node is None:return Noneelse:return node.parentdef children_count(self, node):"""获取子节点个数"""return 2 - [node.left, node.right].count(self.black_leaf)def draw_img(self, img_name='Red_Black_Tree.png'):"""用pygraphviz画红黑树"""if self.tree is None:returntree = pgv.AGraph(directed=True, strict=True)queue = deque([self.tree])num = 0while queue:e = queue.popleft()if e != self.black_leaf:  # 黑色叶子的连线由各个节点自己画tree.add_node(e.data, color=e.color, fontcolor="white", style="filled",fontname="Microsoft YaHei", shape="circle", margin=0)for c in [e.left, e.right]:queue.append(c)if c != self.black_leaf:tree.add_edge(e.data, c.data, color="blue")else:num += 1tree.add_node("nil%s" % num, label="Nil", color="black", fontcolor="white", style="filled",fontname="Microsoft YaHei", shape="circle", margin=0)tree.add_edge(e.data, "nil%s" % num, color="blue")tree.graph_attr['epsilon'] = '0.01'tree.layout('dot')tree.draw(img_name)if __name__ == '__main__':rbt = RedBlackTree()nums = list(range(1, 20))for num in nums:rbt.insert(num)search_num = 7n = rbt.find(search_num)if n:print(n.data)else:print('node {} not found'.format(search_num))rbt.delete(4)rbt.draw_img()

红黑树可视化效果

至此我们就完成了红黑树的可视化。


http://chatgpt.dhexx.cn/article/FCbsmCdm.shtml

相关文章

到底什么是红黑树?

到底什么是红黑树&#xff1f; 首先&#xff0c;可以这么简单粗暴的理解&#xff0c;红黑树≈平衡的二叉排序树。 那么很显然&#xff0c;要想弄懂红黑树&#xff0c;得先明白什么是树、二叉树、二叉排序树、平衡树以及不平衡树。下面我们一个个来了解。 1.第一个问题&#x…

面试常问:什么是红黑树?

什么是红黑树&#xff1f; ———————————— 二叉查找树&#xff08;BST&#xff09;具备什么特性呢&#xff1f; 1.左子树上所有结点的值均小于或等于它的根结点的值。 2.右子树上所有结点的值均大于或等于它的根结点的值。 3.左、右子树也分别为二叉排序树。 下图…

红黑树简介

一、什么是红黑树 红黑树&#xff08;Red Black Tree&#xff09; 是一种自平衡二叉查找树&#xff0c;是在计算机科学中用到的一种数据结构&#xff0c;典型的用途是实现关联数组。 红黑树是在1972年由Rudolf Bayer发明的&#xff0c;当时被称为平衡二叉B树&#xff08;symme…

什么是红黑树?

一、红黑树的产生 1.二叉树 定义&#xff1a;二叉树&#xff08;binary tree&#xff09;是指树中节点的度不大于2的有序树&#xff0c;它是一种最简单且最重要的树。二叉树的递归定义为&#xff1a;二叉树是一棵空树&#xff0c;或者是一棵由一个根节点和两棵互不相交的&…

说说什么是红黑树?

分析&回答 红黑树介绍 R-B Tree&#xff0c;全称是Red-Black Tree&#xff0c;又称为“红黑树”&#xff0c;红黑树就是一种平衡的二叉查找树&#xff0c;说他平衡的意思是他不会变成“瘸子”&#xff0c;左腿特别长或者右腿特别长。除了符合二叉查找树的特性之外&#xf…

漫画:什么是红黑树?(整合版)

前段时间&#xff0c;小灰发布了红黑树相关的文章&#xff0c;分成上下篇来讲解。 这一次&#xff0c;小灰把两篇文章做了整合&#xff0c;并且修正了红黑树删除部分的图片错误&#xff0c;感谢大家的指正。 ————— 第二天 ————— ———————————— 二叉查找…

抗性基因数据库CARD介绍

随着抗生素药物的发现及使用&#xff0c;越来越多的耐药菌株由此产生。而耐药菌株的发展则会增加疾病治疗的难度和成本&#xff0c;因此耐药微生物的研究则显得尤为重要。目前&#xff0c;通过对耐药基因的鉴定挖掘能够一定程度上帮助我们揭开耐药机制&#xff0c;为疾病的治疗…

关于 GIL

目录 GIL是什么GIL产生的原因如何避免受到GIL的影响什么时候会释放GIL锁互斥锁和Gil锁的关系 参考链接 python中的GIL详解 python面试不得不知道的点——GIL 《Python面试每日一题》之GIL Python GIL 是功能和性能之间权衡后的产物&#xff0c;它尤其存在的合理性&#xff0…

gin介绍

Gin的介绍与应用 Gin是一个golang的web框架&#xff0c;Gin像是一些常用函数或者工具的集合。具有快速灵活&#xff0c;容错方便等特点。Gin自身的net/http足够简单&#xff0c;性能也非常不错。 Gin的官网&#xff1a;https://github.com/gin-gonic/gin 安装前保证git和go都安…

gin_gorm

一、使用形式 1、引入gorm包 import "github.com/jinzhu/gorm" 2、导入数据库驱动 import _ "github.com/go-sql-driver/mysql"为了方便记住导入路径&#xff0c;GORM包装了一些驱动&#xff1a; import _ "github.com/jinzhu/gorm/dialects/mysq…

【GIC】配置GIC

本章介绍如何在裸机环境中启用和配置兼容GICv3的中断控制器。 目录 一、全局设置 二、单独PE设置 2.1 Redistributor配置 2.2 CPU接口配置 2.3 PE配置 三、SPI, PPI, SGI配置 3.1设置SPI的目标PE 附&#xff1a;寄存器介绍 附1&#xff1a;GICD_CTLR 附2&#xff1a;…

大寰AG-95夹爪指尖更换、结构示意图及通讯协议转换器说明

注意&#xff1a; 在对手爪进行任何操作之前&#xff0c;请先关闭机器人和夹持电源。 大寰AG-95夹爪指尖更换更换步骤&#xff1a; 1. 使用 M3 内六角螺丝刀卸下指尖螺丝&#xff0c;拆卸磨损的指尖。 2. 清洁手指件并彻底擦干。 3. 取出指尖上φ3x6 mm 定位销。 4. 将…

使用Landsat系列数据来检测喜马拉雅地区的冰湖溃决(Georg Veha等人,RSE,2018)

一、背景 这是一篇做冰湖溃决的文章&#xff0c;作者主要使用了random forest来检测喜马拉雅地区的冰湖溃决现象&#xff0c;这项成果发表在了Remote Sensing of Environment上。 文献连接&#xff1a;https://doi.org/10.1016/j.rse.2017.12.025 文献引用&#xff1a;Georg Ve…

App设计灵感之十二组精美的智能家居App设计案例

通过手机远程启动家里的各种家用电器&#xff0c;让我们回到家后可以享受到舒适的环境&#xff0c;这便是智能家居给我们带来的便利。 ① Smart Hub Application design by Ariuka ② Smart Home by Srgi Mi ③ Smart Home Mobile App by Ghulam Rasool ④ Smart Home Mobile …

基恩士KV8000通过HT3S-EIS-MDN网关与大寰机器人交换数据

一、概述 本文主要介绍使用HI-TOP网关 HT3S-EIS-MDN在基恩士KV8000 PLC和大寰RGI系列旋转抓手之间进行数据交换。 解决的问题&#xff1a;基恩士PLC KV8000监控和大寰RGI系列旋转抓手。 解决方法&#xff1a;使用HI-TOP网关 HT3S-EIS-MDN。基恩士KV8000支持EthterNet/IP协议…

CARD耐药数据库Linux使用

一、背景 关于耐药性有很多数据库可以使用&#xff0c;但是CARD的优势在于数据库较为准确&#xff0c;只有经过文章进行验证后的基因才会被记录 二、软件链接 软件github: GitHub - arpcard/rgi: Resistance Gene Identifier (RGI). Software to predict resistomes from p…

(原创)用红黄蓝RYB色相环(伊登色相环)代替RGB(RGI/RGV)色相环

作者&#xff1a;❄️固态二氧化碳❄️ (主页) 链接&#xff1a;(原创)用红黄蓝RYB色相环(伊登色相环)代替RGB(RGI/RGV)色相环 - 固态二氧化碳的博客 - CSDN博客 来源&#xff1a;CSDN博客 发表时间&#xff1a;2019年05月28日 12:33:57 著作权归作者所有。商业转载请联系作者获…

APT、ET、RGI、ICQ

越来越大的屏幕尺寸和越来越多的需要显示屏常亮应用&#xff0c;要求手机厂商必须从各个方向考虑来提升电池续航时间。一般而言&#xff0c;手机上常用的有两种 PA 的省电技术&#xff0c;一种是平均功率跟踪技术&#xff08;APT&#xff09;&#xff0c;另外一种是包络跟踪&am…

十、ABP

一、官网 安装 安装成功Core 2.2版本的 转载于:https://www.cnblogs.com/fger/p/10641811.html