![Tikz 树形图,带每个级别的注释](https://linux22.com/image/252529/Tikz%20%E6%A0%91%E5%BD%A2%E5%9B%BE%EF%BC%8C%E5%B8%A6%E6%AF%8F%E4%B8%AA%E7%BA%A7%E5%88%AB%E7%9A%84%E6%B3%A8%E9%87%8A.png)
我在使用 tikz 绘制树时遇到了问题。
树是正确的,但“rightarrows”错误地指向与每个树级相关的文本(看起来不太好)。当然,文本也应该不带圆圈显示。
有人可以帮忙吗?
我根据这个例子编写了这棵树:http://www.texample.net/tikz/examples/merge-sort-recursion-tree
我的代码:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{trees}
\begin{document}
\begin{tikzpicture}[level distance=1.5cm,
level 1/.style={sibling distance=3.5cm},
level 2/.style={sibling distance=1cm}]
\tikzstyle{every node}=[circle,draw]
\node [red] {1}
child [red] {
node {3}
child { node {4} }
child [black] { node {5} }
child [black] { node {5} }
}
child {
node {3}
child { node {4} }
child { node {5} }
child { node {5} }
}
child {
node {3}
child { node {4} }
child { node {5} }
child { node {5}
child [grow=right] { node (q) {$\Rightarrow$} edge from parent [draw=none]
child [grow=right] {node (q) {$\textrm{2nd level arrow indicates here}$} edge from parent[draw=none]
child [grow=up] {node (q) {$\Rightarrow$} edge from parent[draw=none]
child [grow=right] {node (r) {$\textrm{1st level arrow indicates here}$} edge from parent[draw=none]
child [grow=up] {node (s) {$\Rightarrow$} edge from parent[draw=none]
child [grow=right] {node (r) {$\textrm{1st level arrow indicates here}$} edge from parent[draw=none]
}
}
}
}
}
}
}
};
\end{tikzpicture}
\end{document}
答案1
您可以利用 TikZ 树节点的一个有趣功能。如果您为根节点命名,例如 ,Root
则树中的所有节点也会自动命名。节点的每个子节点都Root
命名为Root-1
、等等。这些子节点中的每一个都可以依次有子节点,这些子节点使用相同的模式命名。例如,在您的树中,最左边的子节点将被称为,而树的最右边的节点为。Root-2
Root-3
Root-1-1
Root-3-3
一旦知道了这些节点的名称,就可以在相对于它们的坐标处绘制任何内容。例如:
\path (Root) ++(3cm,0) node{Foo};
将文本放置Foo
在3cm
节点处Root
。
如果希望注释垂直对齐,它们应该共享水平坐标。每个注释的位置的一个很好的参考点是其y
部分与注释级别相同,而x
部分与最右侧节点相同的坐标。这些坐标可以使用-|
语法指定。例如,坐标是一条水平线通过和 一条垂直线通过 的(Root -| Root-3-3)
交点。这是下图中用红色标记的点:Root
Root-3-3
以相同的方式(Root-1 -| Root-3-3)
将给出位于相同水平坐标、但位于第二级和(Root-1-1 -| Root-3-3)
第三级的点。
这些坐标可用于定位注释和箭头,如下面的 MWE 所示:
\documentclass{article}
\usepackage{tikz}
\usetikzlibrary{trees}
\begin{document}
\begin{tikzpicture}[level distance=1.5cm,
level 1/.style={sibling distance=3.5cm},
level 2/.style={sibling distance=1cm}]
\tikzstyle{every node}=[circle,draw]
\node (Root) [red] {1}
child [red] {
node {3}
child { node {4} }
child [black] { node {5} }
child [black] { node {5} }
}
child {
node {3}
child { node {4} }
child { node {5} }
child { node {5} }
}
child {
node {3}
child { node {4} }
child { node {5} }
child { node {5} }
};
% Comments to each level
\begin{scope}[every node/.style={right}]
\path (Root -| Root-3-3) ++(5mm,0) node {$\Rightarrow$} ++(5mm,0) node {1st level arrow indicates here};
\path (Root-1 -| Root-3-3) ++(5mm,0) node {$\Rightarrow$} ++(5mm,0) node {2nd level arrow indicates here};
\path (Root-1-1-| Root-3-3) ++(5mm,0) node {$\Rightarrow$} ++(5mm,0) node {3rd level arrow indicates here};
\end{scope}
\end{tikzpicture}
\end{document}
其结果是:
当然,这种重复性任务最好用循环来处理。在这种情况下,我将使用一个名为的变量\text
来循环遍历每个级别的注释,并让循环体计算适当的坐标。我使用了一个卑鄙的技巧,在循环的每次迭代中,将字符串“-1”添加到宏的末尾\level
,这样第一次迭代就会扩展为Root
,第二次扩展为Root-1
,第三次扩展为,Root-1-1
依此类推:
\begin{scope}[every node/.style={right}]
\xdef\level{Root} % Initial top level
\def\rightmostnode{Root-3-3} % Name of the node with greater x coordinate
\foreach \text in {1st level arrow indicates here,
2nd level arrow indicates here,
3rd level arrow indicates here}
{
\path (\level -|\rightmostnode) ++(5mm,0) node{$\Rightarrow$} ++(5mm,0) node {\text};
\xdef\level{\level-1}
}
\end{scope}
请注意,这个“通用”解决方案会产生相同的结果,但比最初的更直接的解决方案更复杂,占用更多行和内存。但我忍不住要展示它。这是程序员的通病。
答案2
您可以使用本问题中描述的技术如何绘制附加到 tikz-qtree 的自定义节点?使用 TikZ 的范围机制将注释绘制为第一棵树旁边的单独树。这样您就可以正确排列级别。对于大多数树,tikz-qtree
包提供了用于输入树本身的更简单的语法。
\documentclass{article}
\usepackage{tikz}
\usepackage{tikz-qtree}
\begin{document}
\begin{tikzpicture}[every tree node/.style={draw,circle},edge from parent path={(\tikzparentnode) -- (\tikzchildnode)},sibling distance=.5cm]
\Tree [.\node[red] (1) {1};
\edge[red]; [.\node[red] (3) {3}; \edge[red];[.\node[red] (4) {4}; ] [.5 ] [.5 ] ]
[.3 [.4 ] [.5 ] [.5 ] ]
[.3 [.4 ] [.5 ] [.5 ] ]
]
\begin{scope}[xshift=3in,every tree node/.style={},edge from parent path={}]
\Tree [.{First level} [.{Second Level} [.{Third level} ]]]
\end{scope}
\end{tikzpicture}
\end{document}
答案3
这是一个 Forest 解决方案。
\documentclass[tikz, border=5pt]{standalone}
\usepackage{forest}
\begin{document}
\begin{forest}
for tree={
circle,
draw,
},
arrow just/.style={
tikz+={
\node [anchor=mid west] at (.mid -| e) {$\Rightarrow$ #1};
},
},
colour me/.style={
draw=#1,
edge={draw=#1},
text=#1,
},
trail/.style={
colour me=#1,
for ancestors={colour me=#1},
},
tikz+={\coordinate (e) at (current bounding box.east);}
[1, arrow just={1st level arrow indicates here}
[3, arrow just={2nd level arrow indicates here}
[4, arrow just={3rd level arrow indicates here}, trail=red]
[5]
[5]
]
[3
[4]
[5]
[5]
]
[3
[4]
[5]
[5]
]
]
\end{forest}
\end{document}
答案4
另一种解决方案是使用istgame
包裹:
\documentclass{standalone}
\usepackage{amsmath}
\usepackage{istgame}
\begin{document}
\begin{istgame}
% tree
\setistOvalNodeStyle{6mm}
\xtdistance{15mm}{35mm}
\istrooto(0)[draw=red,red]{1}
\istb[red]
\istb
\istb
\endist
\xtdistance{15mm}{10mm}
\istrooto(1)(0-1)[draw=red,red]{3}
\istb[red]{}{4}[center,circle,draw,fill=white,red]
\xtShowEndPoints[oval node]
\istb{}{5}[center]
\istb{}{5}[center]
\endist
\istrooto(2)(0-2){3}
\istb{}{4}[center]
\istb{}{5}[center]
\istb{}{5}[center]
\endist
\istrooto(3)(0-3){3}
\istb{}{4}[center]
\istb{}{5}[center]
\istb{}{5}[center]
\endist
% level comments
\coordinate (z) at (3-3);
\coordinate (w) at ([xshift=1cm]z);
\node at (0-|w) [right] {$\Rightarrow$ 1st level arrow indicates here};
\node at (1-|w) [right] {$\Rightarrow$ 2nd level arrow indicates here};
\node at (z-|w) [right] {$\Rightarrow$ 3rd level arrow indicates here};
\end{istgame}
\end{document}
(添加)
或者,您可以用以下代码替换最后一部分(来自 istgame 版本 2.0):
% level comments
\xtTimeLineH[draw=none](0){0}{5.5}{$\Rightarrow$ 1st level arrow indicates here}
\xtTimeLineH[draw=none](1){0}{5.5}{$\Rightarrow$ 2nd level arrow indicates here}
\xtTimeLineH[draw=none](3-3){0}{5.5}{$\Rightarrow$ 3rd level arrow indicates here}