在Graphviz中画出好看的二叉树、B树、如何使用pos变量

本文介绍了如何使用Graphviz的DOT语言在IDEA中绘制规整的二叉树和B树,包括添加隐藏节点、修改节点和箭头属性,解决节点分割方向问题,并详细解释了相关语法。同时,展示了使用pos参数调整节点位置以绘制特定形状的树,并通过实例演示了归并排序的图解过程。
摘要由CSDN通过智能技术生成

一、二叉树

(一) 前提

使用过Graphviz画二叉树的同学都会发现,画出来的东西没有像教材那样规规矩矩,对于有强迫症的同学来说,可以采用以下思路进行完善

(二) IDEA + Dot

@startuml
digraph btree {
    graph [nodesep=0.1, pad=0.02, ranksep=0.5]
    node [shape=circle, style=bold, fontsize=22, fontname=Consolas, width=0.8, height=0.8]
    edge [style=bold]
    34 -> 5
    34 -> 77
    5 -> 3
    n1[width=0.1, height=0.1, shape=point, style=invis]
    5 -> n1[weight=10, style=invis]
    5 -> 22
    77 -> 59
    n2[width=0.1, height=0.1, shape=point, style=invis]
    77 -> n2[weight=10, style=invis]
    77 -> 91
};
@enduml

(三) 结果

(四) 语法说明

1、添加隐藏节点进行占位
  • 在节点3、节点22之间添加节点n1,在节点59、节点91之间添加节点n2
  • 由于Graphviz是按Dot的书写顺序来添加节点的,因此做出以上选择
  • 添加完节点后还需让节点5、节点77分别指向节点n1、节点n2,只有这样才能让n1、n2出现在我们想要的位置处
2、修改节点、箭头属性
  • style=invis:隐藏
  • shape=point:将节点n1、n2从node中定义的circle改为point,能节省更多空间,使其更美观
  • width=0.1, height=0.1:将节点n1、n2进一步缩小
  • weight=10:使节点n1、n2能够杵在中间,不会歪歪斜斜

二、B树

(一) DOT + IDEA

@startuml
digraph BTree {
    graph [nodesep=0.1, pad=0.02, ranksep=0.5, splines=false]
    node [shape=record, style=bold, fontsize=22, fontname=Consolas, width=0.8, height=0.8]
    edge [style=bold]

    Root[label="{M | { <f0> | <f1> }}"];
    Node1[label="{{D | G} | {<f0> | <f1> | <f2>}}"];
    Node2[label="{{Q | T} | { <f0> | <f1> | <f2> }}"];
    Node3[label="{{A | C} | { <f0> | <f1> | <f2> }}"];
    Node4[label="{{E | F} | { <f0> | <f1> | <f2> }}"];
    Node5[label="{{H | K | L} | { <f0> | <f1> | <f2> | <f3> }}"];
    Node6[label="{{N | P} | { | | }}"];
    Node7[label="{{R | S} | { | | }}"];
    Node8[label="{{W | X | Y | Z} | { | | | | }}"];

    Root : f0 : s -> Node1 : n;
    Root : f1 : s -> Node2 : n;
    Node1 : f0 : s -> Node3 : n;
    Node1 : f1 -> Node4 : n[weight=10];
    Node1 : f2 -> Node5 : n;
    Node2 : f0 -> Node6 : n;
    Node2 : f1 -> Node7 : n[weight=10];
    Node2 : f2 : s -> Node8 : n;
}
@enduml

(二) 结果

(三) 遇见的问题(节点分割方向问题)

1、产生的问题

以Root节点为例,一般采用的分割语句为:

Root[label="M | { <f0> | <f1> }"];

但这样所获取的结构并不是我想要的:

我要的是这种:

2、解决方案
(1) 思路一(无法解决)
  • 在graph中添加rankdir=LR,LR表示Left to Right,改变了全局结构的方向
  • 虽可解决上述的小问题,但会导致出现另一个问题:即箭头方向也为LR
  • 最终形成的总体结果完全不理想:
(2) 思路二(无法解决)
  • 考虑到rankdir在graph中,是全部变量,那我就打算将其局部化
  • 将rankdir从graph中去除,加在node里,加在指向里,最后发现毫无卵用
(3) 思路三(成功解决)
  • 知识点:“|”用于分割多列,“{ | }”用于分割多行
  • 了解到这个知识点后,我就想:那将上下作为列,不就可以实现完整的上下分割了吗?
  • 仍以Root节点为例:即在最外层添加1个"{}",将整体作为列
Root[label="{M | { <f0> | <f1> }}"];
  • 通过这种方式实现特定情况下的“节点方向从左到右,全局方向为从上到下”

(四) 语法说明

1、<f0>、<f1>等;n、s等

作为箭头的指针,如:

Root : f0 : s -> Node1 : n;

n表示north,s表示south
含义:箭头从Root节点的f0的南处出发,指向Node1节点的北处

2、splines=false

使用了上方的指针会出现1个问题:箭头变得弯曲,使用该语句可将箭头强制掰直

三、pos参数的使用

(一) 说明

要先通过layout将引擎由dot改为neato
以极端的二叉搜索树为例

(二) NEATO + IDEA

@startuml
digraph Demo {
    layout=neato;
    graph [nodesep=0, pad=0.03, ranksep=0, splines=false];
    node [shape=circle, style=bold, fontsize=22, fontname=Consolas, width=0.7]
    edge [style=bold];

    1[pos="0, 6!"];
    2[pos="1, 5.5!"];
    3[pos="2, 5!"];
    4[pos="3, 4.5!"];
    5[pos="4, 4!"];
    6[pos="5, 3.5!"];
    1 -> 2 -> 3 -> 4 -> 5 -> 6;
}
@enduml

(三) 结果

四、使用pos画树

(一) NETAO + IDEA

使用pos可简化上方的画树过程,以红黑树的插入过程为例

@startuml
digraph RBTInsert {
    layout=neato;
    graph [nodesep=0.1, pad=0.03, ranksep=0.3, splines=false];
    node [shape=circle, style=bold, fontsize=22, fontname=Consolas, fontcolor=white, width=0.7];
    edge [style=bold, fontsize=22, fontname=Consola];

    A1[label=A, pos="2, 4!"];
    B1[label=B, pos="1.5, 3.1!"];
    C1[label=C, pos="2.5, 3.1!"];
    D1[label=D, pos="1, 2.2!"];
    E1[label=E, pos="0.5, 1.3!"];
    F1[label=F, pos="1.5, 1.3!"];
    G1[label=G, pos="0, 0.4!"];
    A1, D1, A2, E2, F2, A3, B3, C3, E3, F3[style=filled, fillcolor=black];
    B1, C1, E1, F1, G1, B2, C2, D2, G2, D3, G3[style=filled, fillcolor=red, color=red];
    A1 -> B1 -> D1 -> E1 -> G1;
    D1 -> F1;
    A1 -> C1;

    A2[label=A, pos="5.5, 4!"];
    B2[label=B, pos="5, 3.1!"];
    C2[label=C, pos="6, 3.1!"];
    D2[label=D, pos="4.5, 2.2!"];
    E2[label=E, pos="4, 1.3!"];
    F2[label=F, pos="5, 1.3!"];
    G2[label=G, pos="3.5, 0.4!"];
    A2 -> B2 -> D2 -> E2 -> G2;
    D2 -> F2;
    A2 -> C2;

    A3[label=A, pos="9, 4!"];
    B3[label=B, pos="8.5, 3.1!"];
    C3[label=C, pos="9.5, 3.1!"];
    D3[label=D, pos="8, 2.2!"];
    E3[label=E, pos="7.5, 1.3!"];
    F3[label=F, pos="8.5, 1.3!"];
    G3[label=G, pos="7, 0.4!"];
    A3 -> B3 -> D3 -> E3 -> G3;
    D3 -> F3;
    A3 -> C3;

    N1[shape=point, pos="2.5, 2.2!", width=0];
    N2[shape=point, pos="3.8, 2.2!", width=0];
    N1 -> N2[headlabel="(1)", labeldistance=5.1, labelangle=-17];
    N3[shape=point, pos="6, 2.2!", width=0];
    N4[shape=point, pos="7.3, 2.2!", width=0];
    N3 -> N4[headlabel="(2)(3)", labeldistance=5.1, labelangle=-17];
}
@enduml

(二) 结果

(三) 说明

N1 -> N2[headlabel="(1)", labeldistance=5.1, labelangle=-17];

N1、N2被我设置为点且不可见
以图示说明labeldistance、labelangle的作用(以使用headlabel为前提):

五、归并排序图解

@startuml
digraph Demo {
    layout=neato;
    graph [nodesep=0, pad=0.03, ranksep=0, splines=false];
    node [shape=rect, style=bold, fontsize=22, fontname=Consolas, width=0.6, height=0.6]
    edge [style=bold];

    A1[label=10, pos="1, 11!"];
    A2[label=6, pos="1.6, 11!"];
    A3[label=4, pos="2.2, 11!"];
    A4[label=2, pos="2.8, 11!"];
    A5[label=15, pos="3.4, 11!"];
    A6[label=9, pos="4, 11!"];

    B1[label=10, pos="0, 10!"];
    B2[label=6, pos="1, 10!"];
    B3[label=4, pos="2, 10!"];
    B4[label=2, pos="3, 10!"];
    B5[label=15, pos="4, 10!"];
    B6[label=9, pos="5, 10!"];
    B1->B2[arrowhead = none];
    B3->B4[arrowhead = none];
    B5->B6[arrowhead = none];

    C1[label=6, pos="0.2, 9!"];
    C2[label=10, pos="0.8, 9!"];
    C3[label=2, pos="2.2, 9!"];
    C4[label=4, pos="2.8, 9!"];
    C5[label=9, pos="4.2, 9!"];
    C6[label=15, pos="4.8, 9!"];
    C2->C3[arrowhead = none];

    D1[label=2, pos="0.8, 8!"];
    D2[label=4, pos="1.4, 8!"];
    D3[label=6, pos="2, 8!"];
    D4[label=10, pos="2.6, 8!"];
    D5[label=9, pos="3.8, 8!"];
    D6[label=15, pos="4.4, 8!"];
    D4->D5[arrowhead = none];

    E1[label=2, pos="1, 7!"];
    E2[label=4, pos="1.6, 7!"];
    E3[label=6, pos="2.2, 7!"];
    E4[label=9, pos="2.8, 7!"];
    E5[label=10, pos="3.4, 7!"];
    E6[label=15, pos="4, 7!"];

    F1[pos="0.5, 10!", style=invis];
    F2[pos="2.5, 10!", style=invis];
    F3[pos="4.5, 10!", style=invis];
    F4[pos="1.5, 9!", style=invis];
    F5[pos="4.5, 9!", style=invis];
    F6[pos="3.2, 8!", style=invis];
    F1 : s -> C2 : nw;
    F2 : s -> C4 : nw;
    F3 : s -> C6 : nw;
    F4 : s -> D2 : ne;
    F5 : s -> D5 : ne;
    F6 : s -> E3 : ne;
}
@enduml
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值