我正在尝试用 tikz 来说明 DFShttp://www.cse.unsw.edu.au/~billw/Justsearch1.gif做。
我想出了
\documentclass[border=10pt]{standalone}
\usepackage{forest}
\begin{document}
\begin{forest}
for tree={circle,draw,fill=white,align=center}
[$a$
[$b$
[$d$
[$h$]
[,phantom]
]
[$e$
[$i$]
[$j$]
]
]
[$c$
[$f$
[,phantom]
[$k$]
]
[$g$]
]
]
\end{forest}
\end{document}
但我不知道如何在树上画出线条/箭头,就像图中那样。
答案1
到目前为止发布的答案都完成了任务。但是,它们没有充分利用 Forest 的工作方式。Forest 已经知道如何深度优先或广度优先地遍历一棵树。事实上,它还知道其他几种遍历树的方法。此外,如果需要,可以将穿过树的进一步路径定义为长节点遍历步骤,然后以相同的方式使用。
这个答案说明了一种使用 Forest 已知的六种方法中的每一种来追踪树中的搜索路径的方法:
- 深度优先 (
tree
) - 广度优先 (
tree breadth-first
) - 儿童优先 (
tree children-first
) - 深度优先逆向 (
tree reversed
) - 广度优先逆向 (
tree breadth-first reversed
) - 儿童优先反转 (
tree children-first reversed
)
这需要在序言中做一些设置工作,但之后可以很容易地通过树追踪路径。
以下是追踪六条演示路径的最终代码:
\begin{treerow}
\tracetree
\tracetree[tree breadth-first]
\tracetree[tree children-first]
\end{treerow}
\begin{treerow}
\tracetree[tree reversed]
\tracetree[tree breadth-first reversed]
\tracetree[tree children-first reversed]
\end{treerow}
treerow
只是一个一次性的环境,让我能够用它制作成排的树standalone
。它什么也不做。
\newenvironment{treerow}{}{}
\standaloneenv{treerow}
\tracetree[]
只是一个针对不同搜索路径重复树的包装器。
\newcommand*\tracetree[1][tree]{%
\begin{forest}
circles tree,
tracing tree=#1,
[$a$
[$b$
[$d$
[$h$]
[,phantom]
]
[$e$
[$i$]
[$j$]
]
]
[$c$
[$f$
[,phantom]
[$k$]
]
[$g$]
]
]
\end{forest}
}
请注意,确实应该有%
关注者\end{forest}
,但如果我包含 PDF,Okular 真的不喜欢它。(我不知道为什么,但它的回应让我的眼睛发亮。)
实质性的东西都在风格里
tracing tree=<nodewalk step i.e. search path>
circles tree
就是你的风格,带有圆圈等等。
配置如下:
\forestset{%
我们使用一个键列表来保存搜索路径中的节点列表。
declare keylist register={through},
through={},
这就是你的风格。
circles tree/.style={%
for tree={circle, draw, fill=white, align=center},
},
这是tracing tree
。
tracing tree/.style={%
我们等到树被解析后,Forest 才知道有哪些节点。
delay={%
#1
是指定搜索路径的参数,这是一个较长的 nodewalk 步骤。
for #1={%
我们按照指定的顺序遍历树,将每个节点添加到我们的键列表中,除非它是一个phantom
节点,当我们想跳过它时。
if phantom={}{through+/.option=name},
}
},
再等一下。我们可以早点这样做,但没有真正的理由这样做。
before drawing tree={%
我们设置了一些东西,以便 Foresttikz
在树绘制完成后执行一些操作。这将遍历我们键列表中的节点,through
并在每个节点和前一个节点之间绘制一个虚线箭头(当然,除非没有前一个节点)。
tikz+/.wrap pgfmath arg={%
\foreach \i [count=\j, remember=\i as \k] in {##1} \ifnum\j>1 \draw [densely dashed, -Stealth] (\k.west) -- (\i.west)\fi;
}{(through)}
},
}
就是这样。
}
显然,这可以通过微调来改善间距等,但使用 Forest 已经知道的通过树的搜索路径的知识应该可以节省大量的指定工作。
完整代码:
\documentclass[border=10pt]{standalone}
\usepackage{forest}
\usetikzlibrary{arrows.meta}
\forestset{%
declare keylist register={through},
through={},
circles tree/.style={%
for tree={circle, draw, fill=white, align=center},
},
tracing tree/.style={%
delay={%
for #1={%
if phantom={}{through+/.option=name},
}
},
before drawing tree={%
tikz+/.wrap pgfmath arg={%
\foreach \i [count=\j, remember=\i as \k] in {##1} \ifnum\j>1 \draw [densely dashed, -Stealth] (\k.west) -- (\i.west)\fi;
}{(through)}
},
}
}
\newcommand*\tracetree[1][tree]{%
\begin{forest}
circles tree,
tracing tree=#1,
[$a$
[$b$
[$d$
[$h$]
[,phantom]
]
[$e$
[$i$]
[$j$]
]
]
[$c$
[$f$
[,phantom]
[$k$]
]
[$g$]
]
]
\end{forest}
}
\newenvironment{treerow}{}{}
\standaloneenv{treerow}
\begin{document}
\begin{treerow}
\tracetree
\tracetree[tree breadth-first]
\tracetree[tree children-first]
\end{treerow}
\begin{treerow}
\tracetree[tree reversed]
\tracetree[tree breadth-first reversed]
\tracetree[tree children-first reversed]
\end{treerow}
\end{document}
答案2
您可以将具有 的森林节点作为(!r)
根节点(!1)
,(!2)
将具有 的森林节点作为第 1 级节点(逆时针方向)。对于第 2 级,您可以引用具有(!11)
、(!12)
等的节点。
这是在图书馆的帮助下进行的一次尝试 calc
。
\documentclass[border=10pt]{standalone}
\usepackage{forest}
\usetikzlibrary{arrows.meta,calc}
\begin{document}
\tikzset{-->/.style={dashed,->,arrows=-Latex},
refract1/.style={to path={-- ++(-0.5,-.5)-- (\tikztotarget)}},
refract2/.style={to path={-- ($(\tikztotarget)+(120:7mm)$)--(\tikztotarget)}}}
\begin{forest}
for tree={circle,draw,fill=white,align=center,edge={arrows=-Latex}}
[$a$
[$b$
[$d$
[$h$]
[,phantom]
]
[$e$
[$i$]
[$j$]
]
]
[$c$
[$f$
[,phantom]
[$k$]
]
[$g$]
]
]
\path [-->]
(!r.west) edge (!1.north)
(!1) edge (!2)
(!2.200) edge ($(!11)+(120:7mm)$)
($(!11)+(120:7mm)$) edge[refract1] (!11)
(!11) edge (!12)
(!12) edge (!21)
(!21) edge (!22)
(!22.220) edge[refract2] (!111)
(!111) edge (!121)
(!121) edge (!122)
(!122) edge (!212);
\end{forest}
\end{document}
答案3
您可以命名树的节点,然后在\draw
命令中使用这些名称,就像这样。
\documentclass[border=10pt]{standalone}
\usepackage{forest}
\begin{document}
\begin{forest}
for tree={circle,draw,fill=white,align=center}
[$a$,name=a
[$b$,name=b
[$d$,name=d
[$h$,name=h]
[,phantom]
]
[$e$,name=e
[$i$,name=i]
[$j$,name=j]
]
]
[$c$,name=c
[$f$,name=f
[,phantom]
[$k$,name=k]
]
[$g$,name=g]
]
]
\draw[->,dotted,thick,relative,red] (a) to[out=-20,in=200] (b);
\begin{scope}[->,dashed,thick,blue]
\draw ([shift={(-0.1,0.1)}]a.north west) -- ([shift={(-0.1,0.1)}]b.north west);
\draw ([shift={(-0.1,0.1)}]b.north west) -- ([shift={(-0.1,0.1)}]d.north west);
\end{scope}
\end{forest}
\end{document}