0__*_*0__ 19 binary-tree dot graphviz
我正在尝试使用GraphViz重新创建二叉搜索树的示例图.这是它最终应该看起来的样子:

这是我的第一次尝试:
digraph G {
nodesep=0.3;
ranksep=0.2;
margin=0.1;
node [shape=circle];
edge [arrowsize=0.8];
6 -> 4;
6 -> 11;
4 -> 2;
4 -> 5;
2 -> 1;
2 -> 3;
11 -> 8;
11 -> 14;
8 -> 7;
8 -> 10;
10 -> 9;
14 -> 13;
14 -> 16;
13 -> 12;
16 -> 15;
16 -> 17;
}
Run Code Online (Sandbox Code Playgroud)
但不幸的是GraphViz并不关心树的水平位置,所以我得到:

如何添加约束以使顶点的水平位置反映其总排序?
mar*_*pet 23
您可以按照通常的方法添加不可见的节点和不可见的边缘,并使用边缘权重等,如graphviz FAQ中关于平衡树的建议.在一些简单的情况下,这就足够了.
但是有一个更好的解决方案:Graphviz附带了一个名为gvpr(图形模式扫描和处理语言)的工具
将输入图复制到其输出,可能转换其结构和属性,创建新图或打印任意信息
而且由于Emden R. Gansner已经完成了所有的工作,创建了一个可以很好地布局二进制树的脚本,所以如何实现(所有功劳都归功于ERG):
将以下gvpr脚本保存到名为的文件中tree.gv:
BEGIN {
double tw[node_t]; // width of tree rooted at node
double nw[node_t]; // width of node
double xoff[node_t]; // x offset of root from left side of its tree
double sp = 36; // extra space between left and right subtrees
double wd, w, w1, w2;
double x, y, z;
edge_t e1, e2;
node_t n;
}
BEG_G {
$.bb = "";
$tvtype=TV_postfwd; // visit root after all children visited
}
N {
sscanf ($.width, "%f", &w);
w *= 72; // convert inches to points
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; // visit root first, then children
}
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);
}
}
Run Code Online (Sandbox Code Playgroud)
假设您调用包含图表的点文件binarytree.gv,您可以执行以下行:
dot binarytree.gv | gvpr -c -ftree.gv | neato -n -Tpng -o binarytree.png
Run Code Online (Sandbox Code Playgroud)
结果是:

通过在脚本中切换一行或两行,您甚至可以让单个子节点转到左侧而不是右侧.