如何修复此 TikZ 图片导致箭头偏离表格单元格中心的问题

如何修复此 TikZ 图片导致箭头偏离表格单元格中心的问题

我正在尝试在 LaTeX 中表示一个实现地图数据结构的二叉树。有人告诉我 TikZ 是实现此目的的最佳工具,但我无法实现我的目标。我觉得 TikZ...太难用了?

在探索了很多替代方案之后,这是我第一次认为我接近一个可接受的解决方案,至少对于这棵树来说是这样。

\documentclass{article}
\usepackage{tikz}
\usepackage{array}

\usetikzlibrary{arrows}

\begin{document}

\newcolumntype{x}[1]{>{\centering\arraybackslash\hspace{0pt}}p{#1}}

\newcommand{\tnode}[2]{
    \begin{tabular}{x{2em}|x{2em}}
        \tiny{}clave   & \tiny{}dato \\
        $#1$           & $#2$        \\\hline
        \tiny{}izq     & \tiny{}der  \\
                       &      
    \end{tabular}}

\newcommand{\leftedge}{[edge from parent path = {
                           ([xshift=0px, yshift=0px]\tikzparentnode.center)
                        -- (\tikzchildnode.north)}]}

\newcommand{\rightedge}{[edge from parent path = {
                           ([xshift=0px, yshift=-0px]\tikzparentnode.center)
                        -- (\tikzchildnode.north)}]}

\begin{tikzpicture}[
    level distance=7em,
    every node/.style = {align=center, font=\ttfamily, 
        inner sep=0pt, draw,},
    level 1/.style={sibling distance=20em},
    level 2/.style={sibling distance=12em},
    level 3/.style={sibling distance=6em},
    thick, *->, shorten >=2px, >=latex,
    ]
    \node (root) {\tnode{5}{15}}
        child[left] { node {\tnode{1}{11}} \leftedge{}
            child[right] { node {\tnode{3}{13}} \rightedge{}
                child[left]  { node {\tnode{2}{12}} \leftedge{}}
                child[right] { node {\tnode{4}{14}} \rightedge{}}
            }
        }
        child[right] { node {\tnode{7}{17}} \rightedge{}
            child[left] { node {\tnode{6}{16}} \leftedge{}}
        };
\end{tikzpicture}

\end{document}

我需要指向左子节点的箭头从相应izq四分之一的中心出发,右子节点和 也需要同样的箭头der。但我不知道如何在 中设置这些锚点\tnode。因此,我决定接受使用手动设置的像素数量从众所周知的锚点(即节点中心)偏移。

但后来我发现我想要作为起始点的黑色圆圈并不以边缘起点为中心,因此移动它的圆圈位置并没有以我的目的所期望的方式固定。

我知道 TikZ 中存在多部分节点,还有一些叫做 的东西matrix。 我\tikzmark也知道存在(用于定义新锚点),但它似乎与 配合得不好\tabular

在这种绝望的时刻,我只想用我能理解的方式修复我的代码!

我读过了应该是一个解决方案,但是很抱歉,我不明白。

有没有更简单的解决方案?或者有人能帮我按照链接的图片修复我的图片吗?

谢谢,并且很抱歉我的问题质量较低。

答案1

我认为你想要类似下面的东西:

\documentclass[border=10pt]{standalone}
\usepackage{tikz}
\usetikzlibrary{arrows.meta, matrix}

\newcommand{\matrixbody}[2]{
    \ttfamily\tiny clave \& \ttfamily\tiny dato \\ 
    $#1$                 \& $#2$                \\
    \ttfamily\tiny izq   \& \ttfamily\tiny der  \\
    {}                   \& {}                  \\
}

\tikzset{
    my matrix/.style={
        ampersand replacement=\&,
        matrix of nodes, 
        every node/.style={
            text width=3em,
            minimum height=1.25em,
            inner sep=0pt,
            align=center,
            execute at begin node={\strut} 
        },
        draw,
        thick,
        inner sep=0pt,
        row 3/.style={
            execute at end cell={
                \draw[very thin] 
                    (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.north west) -- 
                    (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.north east);
            }
        },
        column 2/.style={
            execute at end cell={
                \draw[very thin] 
                    (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.north west) -- 
                    (\tikzmatrixname-\the\pgfmatrixcurrentrow-\the\pgfmatrixcurrentcolumn.south west);
            }
        }
    },
    my tree/.style={
        every node/.style={
            my matrix
        },
        level distance=7em,
        level 1/.style={sibling distance=25em},
        level 2/.style={sibling distance=10em},
        level 3/.style={sibling distance=7.5em},
        edge from parent/.style={
            draw,
            thick, 
            {Circle[width=4pt, length=4pt]}-{Latex[]}, 
            shorten >=2pt, 
            shorten <=-2pt, 
        },
        left/.style={
            edge from parent path={
                (\tikzparentnode-4-1.center) --
                (\tikzchildnode.north)
            }
        },
        right/.style={
            edge from parent path={
                (\tikzparentnode-4-2.center) --
                (\tikzchildnode.north)
            }
        }
    },
}

\begin{document}
\begin{tikzpicture}[my tree]
    \node (root) {\matrixbody{5}{15}}
        child[left] { node {\matrixbody{1}{11}} 
            child[missing]
            child[right] { node {\matrixbody{3}{13}} 
                child[left]  { node {\matrixbody{2}{12}} } 
                child[right] { node {\matrixbody{4}{14}} } 
            }
        }
        child[right] { node {\matrixbody{7}{17}} 
            child[left] { node {\matrixbody{6}{16}} } 
            child[missing]
        };
\end{tikzpicture}
\end{document}

在此处输入图片描述

首先,正如你所说,在这里使用矩阵可能是一个好主意。我使用了以下方法这个很好的答案在矩阵内添加细线。使用另一种更复杂的方法也可能实现这一点,但我认为这个想法仍然相当简单。

现在,树的节点本质上是外观非常相似的矩阵,我认为简化其内容的排版是一个好主意,这就是我为此创建自定义命令的原因。请注意,您需要退出&并使用ampersand replacement键才能使其工作。

最后,我用的是方法你链接将圆圈精确地定位在连接箭头的起点处。为此,最好使用库arrows.meta,因为您可以精确指定小圆圈的大小,这对于将选项设置shorten <为正确的值是必要的。

为了使节点放置得更漂亮一些,我插入了两个missing子节点。

答案2

这是一个 Forest 解决方案。

\cdid构成每个矩阵。其样式设置为第一行和第三行以及第二行和第四行大小相等,这样中间的十字也位于几何中心,只需使用矩阵的四个主罗盘锚点即可轻松绘制。

由于我还没有找到在森林节点内的矩阵内引用命名坐标/节点的解决方案 - 即将节点行走!u与后缀-1-2- 相结合,我通过根据父节点计算来确定边的起点。

代码

\documentclass{standalone}
\usepackage{forest}
\usetikzlibrary{positioning, ext.arrows}
\tikzset{
  cdid text/.style={
    shape=rectangle, node font=\tiny\ttfamily, text depth=+0pt, text height=+1em,
    text width=width("clave"), align=center},
  cdid val/.style={shape=rectangle, text width=width("00"), align=center},
  cdid matrix/.style={
    draw, thin, every outer matrix/.append style={inner sep=+0pt},
    every cell/.append code=\pgfpositionnodelater\relax, % disable forest
    append after command={% simple cross
      (\tikzlastnode.west) edge[very thin] (\tikzlastnode.east)
      (\tikzlastnode.north) edge[very thin] (\tikzlastnode.south)}}}
\newcommand*\cdid[2]{%
  \node[cdid text]{clave};\pgfmatrixnextcell\node[cdid text]{dato};\pgfmatrixendrow
  \node[cdid val]{$#1$};  \pgfmatrixnextcell\node[cdid val]{$#2$}; \pgfmatrixendrow
  \node[cdid text]{izq};  \pgfmatrixnextcell\node[cdid text]{der}; \pgfmatrixendrow
  \node[cdid val]{\phantom{$#1$}};\pgfmatrixnextcell
  \node[cdid val]{\phantom{$#2$}};\pgfmatrixendrow}
\forestset{
  cdid tree/.style={
    for tree={
      node options={matrix, cdid matrix},
      before typesetting nodes={+content=\cdid},
      edge={arrows={Centered Circle[scale=.75]}-Latex},
      edge path={
        \noexpand\path[path only](!u.south west)--
          coordinate[pos=\forestoption{n}>1?.75:.25](@) (!u.south east);
        \noexpand\path[\forestoption{edge}]([yshift=1ex]@)--
          (.child anchor)\forestoption{edge label};}},
    phantom/.append style={content=00}}}
\begin{document}
\begin{forest} cdid tree
[{5}{15}
  [{1}{11}
    [,phantom]
    [{3}{13}
      [{2}{12}]
      [{4}{14}]
    ]
  ]
  [{7}{17}
    [{6}{16}]
    [,phantom]
  ]
]
\end{forest}
\end{document}

输出

在此处输入图片描述

答案3

这是另一种方法,几乎​​和你的方法一样。我的策略与你的非常相似。以下是两个(半)步骤的改变:

  • 将父母的锚点从.south改为.south west
  • 用极坐标代替笛卡尔坐标中的移位(更容易形象化)
  • 稍微调整角度和半径
% ~~~ change anchor point AND apply shift in polar coordinates ~~~~~~~~~~
\newcommand{\leftedge}{[edge from parent path = {
                           ([shift=(40:6mm)]\tikzparentnode.south west)
                        -- (\tikzchildnode.north)}]}

结果:

结果

选择:

  • 保持锚点.center
  • 再次在极坐标中将其向外移动

有关极角的信息仅供读者参考:

  • 0 度 == 正 x 方向(向右)
  • 90 度 == 正 y 方向(向上)
  • 180 度 = 负 x 方向(向左)
  • 270 度 = 负 y 方向(向下)
\documentclass{article}
\usepackage{tikz}
\usepackage{array}

\usetikzlibrary{arrows}

\begin{document}

\newcolumntype{x}[1]{>{\centering\arraybackslash\hspace{0pt}}p{#1}}

\newcommand{\tnode}[2]{
    \begin{tabular}{x{2em}|x{2em}}
        \tiny{}clave   & \tiny{}dato \\
        $#1$           & $#2$        \\\hline
        \tiny{}izq     & \tiny{}der  \\
                       &      
    \end{tabular}}

% ~~~ change anchor point AND apply shift in polar coordinates ~~~~~~~~~~
\newcommand{\leftedge}{[edge from parent path = {
                           ([shift=(40:6mm)]\tikzparentnode.south west)
                        -- (\tikzchildnode.north)}]}

\newcommand{\rightedge}{[edge from parent path = {
                           ([shift=(140:6mm)]\tikzparentnode.south east)
                        -- (\tikzchildnode.north)}]}

\begin{tikzpicture}[
    level distance=7em,
    every node/.style = {align=center, font=\ttfamily, 
        inner sep=0pt, draw,},
    level 1/.style={sibling distance=20em},
    level 2/.style={sibling distance=12em},
    level 3/.style={sibling distance=6em},
    thick, *->, shorten >=2px, >=latex,
    ]
    \node (root) {\tnode{5}{15}}
        child[left] { node {\tnode{1}{11}} \leftedge{}
            child[right] { node {\tnode{3}{13}} \rightedge{}
                child[left]  { node {\tnode{2}{12}} \leftedge{}}
                child[right] { node {\tnode{4}{14}} \rightedge{}}
            }
        }
        child[right] { node {\tnode{7}{17}} \rightedge{}
            child[left] { node {\tnode{6}{16}} \leftedge{}}
        };
\end{tikzpicture}

\end{document}

相关内容