在 tikz 森林顶部画线

在 tikz 森林顶部画线

我正在尝试用 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 已知的六种方法中的每一种来追踪树中的搜索路径的方法:

  1. 深度优先 ( tree)
  2. 广度优先 ( tree breadth-first)
  3. 儿童优先 ( tree children-first)
  4. 深度优先逆向 ( tree reversed)
  5. 广度优先逆向 ( tree breadth-first reversed)
  6. 儿童优先反转 ( 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}

在此处输入图片描述

相关内容