一、二叉树
(一) 前提
使用过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