您可以按照
Graphviz关于平衡树的FAQ中提出的常规方法,添加不可见节点和不可见边,并调整边权等。在一些
简单情况下,这已经足够了。
但是有一个更好的解决方案:Graphviz附带了一个名为
gvpr(图形模式扫描和处理语言)的工具,它允许
将输入图形复制到其输出,可能转换其结构和属性,创建新图形或打印任意信息
由于
Emden R. Gansner已经通过创建一个脚本来很好地布局二叉树,因此以下是如何执行此操作的步骤(所有功劳归功于ERG):
将以下gvpr脚本保存到名为
tree.gv
的文件中:
BEGIN {
double tw[node_t];
double nw[node_t];
double xoff[node_t];
double sp = 36;
double wd, w, w1, w2;
double x, y, z;
edge_t e1, e2;
node_t n;
}
BEG_G {
$.bb = "";
$tvtype=TV_postfwd;
}
N {
sscanf ($.width, "%f", &w);
w *= 72;
nw[$] = w;
if ($.outdegree == 0) {
tw[$] = w;
xoff[$] = w/2.0;
}
else if ($.outdegree == 1) {
e1 = fstout($);
w1 = tw[e1.head];
tw[$] = w1 + (sp+w)/2.0;
if (e1.side == "left")
xoff[$] = tw[$] - w/2.0;
else
xoff[$] = w/2.0;
}
else {
e1 = fstout($);
w1 = tw[e1.head];
e2 = nxtout(e1);
w2 = tw[e2.head];
wd = w1 + w2 + sp;
if (w > wd)
wd = w;
tw[$] = wd;
xoff[$] = w1 + sp/2.0;
}
}
BEG_G {
$tvtype=TV_fwd;
}
N {
if ($.indegree == 0) {
sscanf ($.pos, "%f,%f", &x, &y);
$.pos = sprintf("0,%f", y);
}
if ($.outdegree == 0) return;
sscanf ($.pos, "%f,%f", &x, &y);
wd = tw[$];
e1 = fstout($);
n = e1.head;
sscanf (n.pos, "%f,%f", &z, &y);
if ($.outdegree == 1) {
if (e1.side == "left")
n.pos = sprintf("%f,%f", x - tw[n] - sp/2.0 + xoff[n], y);
else
n.pos = sprintf("%f,%f", x + sp/2.0 + xoff[n], y);
}
else {
n.pos = sprintf("%f,%f", x - tw[n] - sp/2.0 + xoff[n], y);
e2 = nxtout(e1);
n = e2.head;
sscanf (n.pos, "%f,%f", &z, &y);
n.pos = sprintf("%f,%f", x + sp/2.0 + xoff[n], y);
}
}
假设您的包含图表的dot文件名为binarytree.gv
,则可以执行以下命令:
dot binarytree.gv | gvpr -c -ftree.gv | neato -n -Tpng -o binarytree.png
结果是:
通过在脚本中切换一行或两行,甚至可以使单个子节点向左而不是右侧。
gvpr: "./tree.gv", line 46: _nd_a0:<<< -- syntax error
(我已经验证代码已经正确复制,也尝试从Gansner的帖子中复制)。现在我不知道那意味着什么?我在第46行看不到任何可疑的地方 :-( - 0__N { }
块,如果我删除它,错误就会消失,但布局也不会完成。这可能是版本问题吗?我的gvpr version 2.26.3 (20100126.1600)
。 - 0__