我正在使用 TikZ 在 latex 中绘制递归树,我的问题是一些圆圈重叠,我似乎无法弄清楚原因。此外,有没有办法在连接父节点和其子节点的线上放置一些文本?
这是我的代码。
\documentclass[11pt,a4paper,oneside]{article}
\usepackage{fullpage}
\usepackage[italian]{babel}
\usepackage[utf8]{inputenc}
\usepackage{amsfonts}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsthm}
\usepackage{tikz}
\begin{document}
\begin{figure}[hb]
\centering
\begin{tikzpicture}[level/.style={sibling distance=30mm/#1}]
\node[circle,draw](z){$n$}
child{node [circle,draw] (a) {$\frac{n}{4}$}
child{node [circle,draw] (d) {$\frac{n}{4^2}$}}
child{node [circle,draw] (e) {$\frac{n}{4^2}$}}
child{node [circle,draw] (f) {$\frac{n}{4^2}$}}
}
child{node [circle,draw] (b) {$\frac{n}{4}$}
child{node [circle,draw] (g) {$\frac{n}{4^2}$}}
child{node [circle,draw] (h) {$\frac{n}{4^2}$}}
child{node [circle,draw] (k) {$\frac{n}{4^2}$}}
}
child{node [circle,draw] (c) {$\frac{n}{4}$}
child{node [circle,draw] (l) {$\frac{n}{4^2}$}}
child{node [circle,draw] (m) {$\frac{n}{4^2}$}}
child{node [circle,draw] (n) {$\frac{n}{4^2}$}}
};
\path (a) -- (b) node [midway] {+};
\path (b) -- (c) node [midway] {+};
\path (d) -- (e) node [midway] {+};
\path (e) -- (f) node [midway] {+};
\path (f) -- (g) node [midway] {+};
\path (g) -- (h) node [midway] {+};
\path (h) -- (k) node [midway] {+};
\path (k) -- (l) node [midway] {+};
\path (l) -- (m) node [midway] {+};
\path (m) -- (n) node [midway] {+};
\end{tikzpicture}
\end{figure}
\end{document}
答案1
您还可以使用forest
它来为您计算最小距离。
注意:我确信,作为递归图,以下代码可以简化,但我不知道该怎么做。这只是一个易于理解的解决方案。
\documentclass[11pt, a4paper,oneside]{article}
\usepackage{fullpage}
\usepackage[italian]{babel}
\usepackage[utf8]{inputenc}
\usepackage{amsfonts}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsthm}
\usepackage{forest}
\begin{document}
\begin{figure}[hb]
\centering
\begin{forest}
[$n$, for tree={circle, draw}
[$\frac{n}{4}$, name=a
[$\frac{n}{4^2}$, name=d]
[$\frac{n}{4^2}$, name=e]
[$\frac{n}{4^2}$, name=f]]
[$\frac{n}{4}$, name=b
[$\frac{n}{4^2}$, name=g]
[$\frac{n}{4^2}$, name=h]
[$\frac{n}{4^2}$, name=i]]
[$\frac{n}{4}$, name=c
[$\frac{n}{4^2}$, name=j]
[$\frac{n}{4^2}$, name=k]
[$\frac{n}{4^2}$, name=l]]]
\path (a) -- (b) node [midway] {+};
\path (b) -- (c) node [midway] {+};
\path (d) -- (e) node [midway] {+};
\path (e) -- (f) node [midway] {+};
\path (f) -- (g) node [midway] {+};
\path (g) -- (h) node [midway] {+};
\path (h) -- (i) node [midway] {+};
\path (i) -- (j) node [midway] {+};
\path (j) -- (k) node [midway] {+};
\path (k) -- (l) node [midway] {+};
\end{forest}
\end{figure}
\end{document}
更新:遵循 cfr 建议,math content
并s sep
已引入。
\documentclass[11pt, a4paper,oneside]{article}
\usepackage{fullpage}
\usepackage[italian]{babel}
\usepackage[utf8]{inputenc}
\usepackage{amsfonts}
\usepackage{amsmath}
\usepackage{amssymb}
\usepackage{amsthm}
\usepackage{forest}
\begin{document}
\begin{figure}[hb]
\centering
\begin{forest}
[n, for tree={circle, draw, math content, s sep=1em}
[\frac{n}{4}, name=a
[\frac{n}{4^2}, name=d]
[\frac{n}{4^2}, name=e]
[\frac{n}{4^2}, name=f]]
[\frac{n}{4}, name=b
[\frac{n}{4^2}, name=g]
[\frac{n}{4^2}, name=h]
[\frac{n}{4^2}, name=i]]
[\frac{n}{4}, name=c
[\frac{n}{4^2}, name=j]
[\frac{n}{4^2}, name=k]
[\frac{n}{4^2}, name=l]]]
\path (a) -- (b) node [midway] {+};
\path (b) -- (c) node [midway] {+};
\path (d) -- (e) node [midway] {+};
\path (e) -- (f) node [midway] {+};
\path (f) -- (g) node [midway] {+};
\path (g) -- (h) node [midway] {+};
\path (h) -- (i) node [midway] {+};
\path (i) -- (j) node [midway] {+};
\path (j) -- (k) node [midway] {+};
\path (k) -- (l) node [midway] {+};
\end{forest}
\end{figure}
\end{document}
答案2
对于重叠节点,重要的观察是sibling distance
指定同一父节点的子节点之间的间距,而不是给定级别上所有节点之间的间距。您的示例中的相关行是
\begin{tikzpicture}[level/.style={sibling distance=30mm/#1}]
指定第 n 级的兄弟节点(同一节点的子节点)应彼此间隔 30/n 毫米。因此,第 1 级(第 0 级为根节点)的节点相距 30 毫米。同时,第 2 级的右子节点位于其父节点右侧 15 毫米处,左子节点位于其父节点左侧 15 毫米处它是父母,它和父母的左兄弟的右孩子位于同一位置。
要解决此问题,您需要更改该行。例如,如果您希望拥有一个具有多层的完整三叉树,并且希望第 n 层上的 3^n 个节点均匀分布(对于所有层),您可以使用
\begin{tikzpicture}[level/.style={sibling distance=120mm/3^#1}]
(此处 120mm 似乎对于您拥有的 3 个层来说已经足够了,但您必须增加该缩放因子以避免后续层重叠)。您还可以为每一层明确设置同级距离:
\begin{tikzpicture}[level 1/.style={sibling distance=42mm}, level 2/.style={sibling distance=14mm}]
要向边添加标签,请将其附加edge from parent node {Your Label}
在子节点声明之后,例如,
child{node [circle,draw] (a) {$\frac{n}{4}$} edge from parent node {Your Label}
您可以像任何其他节点一样设置边缘标签节点的样式(例如,使用......edge from parent node[left]
将标签放置在边缘中点的左侧)。
答案3
Ignasi 建议我也许能够“改进”森林解决方案。
我不得不说,这段代码几乎不能被指责为更简单。然而,它更加自动化。森林本身根据两条信息构建树的各个方面:根以下的级别数和每个非终端节点的分支数。
也可以指定分母中使用的因子,但这不是必需的。如果没有给出,则将使用分支数加 1。
默认情况下使用 2 个级别和 3 个分支,如 Ignasi 的示例一样。
要使用该样式,请添加rstyle
到树的序言中。
rbranches=<integer>
指定每个非终端节点的分支数;rlevels=<integer>
指定根以下的级别数。
如果需要覆盖分母中使用的因子,请使用
rdenom=<integer>
。
因此,我们可以绘制如下的3棵树。
首先,我们测试默认设置以匹配 Ignasi 的示例。我增加了一些间距,因为我最初不知道底层有附加符号。
\begin{forest}
rtree,
[]
\end{forest}
现在让我们尝试一棵具有默认分支数但附加一个级别的树,但我们将在这里明确说明所有内容。
\begin{forest}
rtree,
rbranches'=3,
rlevels'=3,
[]
\end{forest}
最后,让我们将分支减少到 2 个,但将级别增加到 5 个。
\begin{forest}
rtree,
rbranches'=2,
rlevels'=5,
[]
\end{forest}
完整代码:
% addaswyd o côd Ignasi: http://tex.stackexchange.com/a/333886/
\documentclass[border=10pt,tikz]{standalone}
\usepackage{forest}
\forestset{
declare count register=rlevels,
declare count register=rbranches,
declare count register=rdenom,
declare count register=rwait,
rlevels'=2,
rbranches'=3,
rdenom'=0,
rwait'=1,
define long step={rup}{}{fake=root,first leaf,ancestors},
rtree/.style={
delay={
repeat/.wrap pgfmath arg={
{##1}{
rwait'+=2,
delay n/.wrap pgfmath arg={
{########1}{
where n children=0{
repeat/.wrap pgfmath arg={
{################1}{
append={[]}
}
}{rbranches},
}{},
}
}{rwait},
}
}{rlevels},
},
before typesetting nodes={
if={(rdenom)==0}{
rdenom/.register=rbranches,
rdenom'+=1,
}{},
for tree={
circle,
draw,
math content,
s sep+=5pt,
},
where level=0{
content=n,
}{
if level=1{
content=\frac{n}{\foresteregister{rdenom}},
}{
content/.wrap pgfmath arg={\frac{n}{\foresteregister{rdenom}^{##1}}}{level()},
},
},
},
before drawing tree={
for rup={
tempkeylista'=,
for nodewalk/.wrap pgfmath arg={
{fake=root,filter={descendants}{(level())==##1}}{tempkeylista/.option=name}
}{level()},
radd/.register=tempkeylista,
},
}
},
radd/.style={
tikz+={
\foreach \i [remember=\i, count=\k, remember=\i as \j] in {#1}
\ifnum\k=1\relax\else\path (\i) -- (\j) node [midway] {$+$}\fi;
},
},
}
\begin{document}
\begin{forest}
rtree,
[]
\end{forest}
\begin{forest}
rtree,
rbranches'=3,
rlevels'=3,
[]
\end{forest}
\begin{forest}
rtree,
rbranches'=2,
rlevels'=5,
[]
\end{forest}
\end{document}
编辑
这说明了如何根据 Forest 填写的模板自动向父节点和子节点之间的边添加标签。出于演示目的,我使用了模板
Step <level of recursion>: Branch <number of branch>
对于大多数标签,文本在分支上方呈一定角度。如果分支数量为奇数,则中间子标签将分成两行并水平放置,后面填充白色,以避免分支穿过标签而显得不美观。
当使用标签时(如在默认情况下),树会稍微间隔开以给标签留出空间。当然,如果模板大小明显不同,则需要进行调整。
可以使用 关闭标签not rtree labels
,或者用 明确打开标签rtree labels
。
使用扩展样式,与上述三棵树相同的代码会自动生成标记的版本。
% ateb: http://tex.stackexchange.com/a/333904/ addaswyd o côd Ignasi: http://tex.stackexchange.com/a/333886/
\documentclass[border=10pt,tikz]{standalone}
\usepackage{forest}
\forestset{
declare count register=rlevels,
declare count register=rbranches,
declare count register=rdenom,
declare count register=rwait,
declare boolean register=rtree labels,
rtree labels,
rlevels'=2,
rbranches'=3,
rdenom'=0,
rwait'=1,
define long step={rup}{}{fake=root,first leaf,ancestors},
rtree/.style={
delay={
repeat/.wrap pgfmath arg={
{##1}{
rwait'+=2,
delay n/.wrap pgfmath arg={
{########1}{
where n children=0{
repeat/.wrap pgfmath arg={
{################1}{
append={[, redge label/.wrap 2 pgfmath args={Step ################################################################1: Branch ################################################################2}{level()}{n()}]}
}
}{rbranches},
}{},
}
}{rwait},
}
}{rlevels},
},
before typesetting nodes={
if={(rdenom)==0}{
rdenom/.register=rbranches,
rdenom'+=1,
}{},
for tree={
circle,
draw,
math content,
s sep+=5pt,
if rtree labels={
l sep+=15pt,
}{},
},
where level=0{
content=n,
}{
if level=1{
content=\frac{n}{\foresteregister{rdenom}},
}{
content/.wrap pgfmath arg={\frac{n}{\foresteregister{rdenom}^{##1}}}{level()},
},
},
},
before packing={
if rtree labels={
where n children=0{
!u.l sep+=30pt,
!u.s sep+=20pt,
if level=1{}{
!uu.l sep+=20pt,
if level=2{}{
!uuu.l sep+=10pt,
},
},
}{},
}{},
},
before drawing tree={
for rup={
tempkeylista'=,
for nodewalk/.wrap pgfmath arg={
{fake=root,filter={descendants}{(level())==##1}}{tempkeylista/.option=name}
}{level()},
radd/.register=tempkeylista,
},
}
},
radd/.style={
tikz+={
\foreach \i [remember=\i, count=\k, remember=\i as \j] in {#1}
\ifnum\k=1\relax\else\path (\i) -- (\j) node [midway] {$+$}\fi;
},
},
redge label/.style={
if rtree labels={
if={(n())<(((rbranches)+1)/2)}{
edge label={node [midway, font=\scriptsize, above, sloped] {#1}},
}{
if={(n())>(((rbranches)+1)/2)}{
edge label={node [midway, font=\scriptsize, above, sloped] {#1}},
}{
temptoksa={#1},
split register={temptoksa}{:}{temptoksb,temptoksc},
edge label/.wrap 2 pgfmath args={node [midway, font=\scriptsize, align=center, fill=white] {##1:\\##2}}{temptoksb}{temptoksc},
},
},
}{},
},
}
\begin{document}
\begin{forest}
rtree,
[]
\end{forest}
\begin{forest}
rtree,
rbranches'=3,
rlevels'=3,
[]
\end{forest}
\begin{forest}
rtree,
rbranches'=2,
rlevels'=5,
[]
\end{forest}
\end{document}