pygraphviz的安装与红黑树可视化
大家好,我是小小明,今天我将带大家安装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()
红黑树可视化效果
至此我们就完成了红黑树的可视化。
pygraphviz的安装与红黑树可视化相关推荐
- 数据结构 - 学习笔记 - 红黑树
数据结构 - 学习笔记 - 红黑树 定义 简介 知识点 1. 结点属性 2. 前驱.后继 3. 旋转 查找 插入 父结点为黑色 父结点为红色 1. 有4种情形只需要变色(对应234树4结点) 1.1. ...
- 红黑树效率为甚恶魔是log_一文带你彻底读懂红黑树(附详细图解)
红黑树简介 红黑树是一种自平衡的二叉查找树,是一种高效的查找树.它是由 Rudolf Bayer 于1972年发明,在当时被称为对称二叉 B 树(symmetric binary B-trees).后 ...
- 红黑树操作详解——很形象的过程
红黑树是一种很好的自平衡二叉排序树,在此,给出一个网友给出的红黑树操作详解: https://segmentfault.com/a/1190000012728513 里面给出了红黑树的详细操作,过程很 ...
- 红黑树和平衡二叉树的区别_一文搞懂红黑树
文章参考 | https://segmentfault.com/a/1190000012728513 前言 当在10亿数据进行不到30次比较就能查找到目标时,不禁感叹编程之魅力! 二叉树 在了解红黑树 ...
- 底层实现红黑树_图解:红黑树
注:本文比较硬核但是很值得大家花心思看完,看完你一定会有所收获的 红黑树是面试中一个很经典也很有难度的知识点,网传字节跳动面试官最喜欢问这个问题.很多人会觉得这个知识点太难,不想花太多功夫去了解,也有 ...
- NGINX下的红黑树源码详解(附 流程图和GIF)
之前博主稍微讲解了下红黑树的原理,那么在这篇博客博主想要把红黑树讲的更加的透彻,以便于更多的人了解红黑树 (本博客会更加详细的介绍之前的博客没介绍到的,所以各位看官不同再回去翻看博主之前那篇红黑树的原 ...
- 红黑树如何快速调整到平衡态_快速多态
红黑树如何快速调整到平衡态 进阶主题 (Advanced Topic) Object-Oriented Programming has ruled the programming world for ...
- MySQL索引数据结构二叉树、红黑树、B-Tree、B+Tree、Hash
索引:帮助MySQL高效获取数据的有序的数据结构. 假设我们有一张表table,包含Clo1和Clo2两个字段 内存地址 Clo1 Clo2 0x07 1 36 0x5A 2 20 0x7A 3 80 ...
- 终于搞懂红黑树!--红黑树的原理及操作
红-黑-树 介绍: 红黑树( Red black tree)是种自平衡二叉查找树,在计算机科学中用到的一种数据结构. 它是在1972年由 Rudolf Bayer发明的当时被称为平衡二叉B树( sym ...
最新文章
- 涉嫌盗取前东家谷歌的代码,一位工程师被判赔1.8亿美金,将全由个人偿还
- MySQL如何从开源中获利
- nodejs 获取post数据
- 图像降噪算法——时域降噪算法
- 从零开始搭建spring-cloud(1) ----eureka
- asp.net中ADO.NET连接SQL数据库代码和连接Access数据库代码
- 员工30年换150万补偿款!佳能珠海关厂 因给太多遭痛骂:恶意拉高赔偿标准
- 提取点位属性文本_手把手教你如何用Python爬取网站文本信息
- 【报告分享】2021年00后生活方式洞察报告.pdf(附下载链接)
- 日期上午下午怎么用date存_Java12都要出来了,你还在使用Date操作日期吗?
- 笔记︱集成学习Ensemble Learning与树模型、Bagging 和 Boosting、模型融合
- mysql常量求和_Mysql之:count(*)、count(常量)、count(字段)的区别
- 框架-vue-cli
- css中的background的几个属性(background-attachment/background-origin,background-clip等)
- 固态硬盘SSD闪存芯片的颗粒类型 和 颗粒等级
- azure mysql on vnet_Azure Database for MySQL (2) VNET rules
- Ubuntu20.04成功安装google浏览器,并正常使用Bing等其他搜索引擎
- 生信文献学习(part1)--PRECISE: a ... approach to transfer predictors of drug response from pre-clinical ...
- composingBuild依赖统一管理实践
- 微信公众平台开发教程第2篇-----微信开发者接入