[Tex/LaTex] Some problems drawing linked lists with TikZ’s matrix library

matricestikz-pgf

Some time ago I found the question Draw an adjacency-list representation of a graph with tikz and wanted to solve it. I could but with some problems which still remain and How should I draw a singly/double linked list? remind me them.

My solution was:

\documentclass{standalone} 
\usepackage{tikz} 
\usetikzlibrary{matrix,arrows} 

\begin{document} 
\begin{tikzpicture}[>=stealth] 
\matrix (M) [matrix of nodes,% 
   column sep=0pt,% 
   row sep=1mm,% 
   nodes={draw, fill=gray!20,%
     minimum width=.5cm, outer sep=0pt,% 
     minimum height=.7cm, anchor=center}, 
   column 1/.style={minimum height=.8cm}]%
{ 
  \mbox{} &[2mm] 2 & \mbox{} &[2mm] 5 & / &[2mm] &   &[2mm] & \\ 
  \mbox{} & 1 & \mbox{} & 5 & \mbox{} & 3 & \mbox{} & 4 & / \\ 
  \mbox{} & 2 & \mbox{} & 5 & \mbox{} & 3 & / & & \\ 
}; 

\foreach \i in {1,2,3}{ 
 \path (M-\i-1) [late options={label=left:\i}]; 
 \draw[->] (M-\i-1)--(M-\i-2.west); 
 \draw[->] (M-\i-3.center)--(M-\i-4.west); 
} 

\draw[->] (M-2-5.center)--(M-2-6.west); 
\draw[->] (M-2-7.center)--(M-2-8.west); 
\draw[->] (M-3-5.center)--(M-3-6.west); 

\begin{scope}[yshift=-3cm]
\matrix (M) [matrix of nodes,% 
   column sep=0pt,% 
   row sep=1mm,% 
   nodes={draw, fill=gray!20,%
    minimum width=.5cm, outer sep=0pt,% 
    minimum height=.7cm, anchor=center}, 
   column 1/.style={minimum height=.8cm}]%
{ 
 |[label=left:1]| \mbox{} &[2mm] 2 &\mbox{}&[2mm] 5 & / &[2mm] &   &[2mm] & \\ 
 |[label=left:2]|\mbox{} & 1 & \mbox{} & 5 & \mbox{} & 3 & \mbox{} & 4 & / \\ 
 |[label=left:3]| \mbox{} & 2 & \mbox{} & 5 & \mbox{} & 3 & / & & \\ 
}; 

\foreach \i in {1,2,3}{ 
% \path (M-\i-1) [late options={label=left:\i}]; 
 \draw[->] (M-\i-1)--(M-\i-2.west); 
 \draw[->] (M-\i-3.center)--(M-\i-4.west); 
} 

\draw[*->] (M-2-5.center)--(M-2-6.west); 
\draw[*->] (M-2-7.center)--(M-2-8.west); 
\draw[*->] (M-3-5.center)--(M-3-6.west); 
\end{scope}
\end{tikzpicture} 
\end{document}

with this results

enter image description here

Now some questions:

1- I used \mbox{} to distinguish 'empty nodes' from 'empty cells'. Do you know a better solution?

2- Although column 1/.style is declared boxes from 1st column show same heigth than other ones. Why?

3- Using labels within nodes (bottom example) changes node (or cell?) center. This problem can be avoided with late options (upper example). Do you know a better option?

4- Looking at first and secon column from upper example it seems that node's name is not equivalent to node.center. Do you know why?

5- I don't know if it's a rendering problem but it looks like adjacent cells have a thicker separation line. What must be column separation to draw border lines one over the other?

6- Solution for How should I draw a singly/double linked list? show starting point of *-> arrows aligned with node center, but here its beahviour is like in Modifying * and o style tikz arrows so that they are centered at the end of line. Coul you explain me why?

Best Answer

  1. You can use {} instead of \mbox{} to save yourself some typing when creating empty nodes. As ipavlic points out in his comment, you can also use the nodes in empty cells key. This means that you can't specify all the extra gaps between the columns in the first row, because that would create a lot of incorrect empty nodes. Rather, you have to specify the extra gaps in the row where they are first used.
  2. You need to use column 1/.style={nodes={minimum height=0.8cm}} to change the node behaviour. If you don't use nodes=..., you are basically changing the options that apply to the matrix as a whole, not the nodes (see Matrix of nodes column style inconsistency for more on that).
  3. It doesn't change the center, but rather the label is assigned the node name (M-1-1). You can prevent this by adding the option every label/.append style={name={}} to the matrix. (see example at the end of the answer for a demonstration)
  4. That's the normal behaviour of specifying a node name without an anchor. Quote from the pgfmanual, section 16.11: "When you say (x)--(1,1), the -- path operation will not draw a line from the center of x, but from the border of x in the direction going towards (1,1)"

  5. In order to make the lines overlap, the distance between the columns has to be -\pgflinewidth. See TikZ matrix as a replacement for tabular.

  6. In How should I draw a singly/double linked list?, the *- line starts at the horizontal position of <node>.two of the multipart node, which is not the center of the second node part, but actually shifted slightly to the left (replace *-> with -- to see where the line actually starts). In the case of the *- arrow style, that's a convenient thing. Since you don't use multipart nodes, but one node for each rectangle, you don't have that anchor, and will have to resort to using shorten < = -2pt.

Here's your code with the suggested changes:

\documentclass{standalone} 
\usepackage{tikz} 
\usetikzlibrary{matrix,arrows,fit} 

\tikzset{circarrow/.style={
        *->,
        shorten <=-2pt
    }
}

\begin{document} 
\begin{tikzpicture}[>=stealth] 
\matrix (M) [matrix of nodes,% 
   column sep=-\pgflinewidth,% 
   row sep=1mm,% 
   nodes in empty cells,
   nodes={draw, fill=gray!20,%
     minimum width=.5cm, outer sep=0pt,% 
     minimum height=.7cm, anchor=center}, 
   column 1/.style={nodes={minimum height=.8cm}}]%
{ 
  &[2mm] 2 & &[2mm] 5 & /  \\ 
 & 1 & & 5 & &[2mm] 3 & &[2mm] 4 & / \\ 
 & 2 & & 5 & & 3 & /\\ 
}; 

\foreach \i in {1,2,3}{ 
 \path (M-\i-1) [late options={label=left:\i}]; 
 \draw[->] (M-\i-1)--(M-\i-2.west); 
 \draw[->] (M-\i-3.center)--(M-\i-4.west); 
} 

\draw[->] (M-2-5.center)--(M-2-6.west); 
\draw[->] (M-2-7.center)--(M-2-8.west); 
\draw[->] (M-3-5.center)--(M-3-6.west); 

\begin{scope}[yshift=-3cm]
\matrix (M) [matrix of nodes,% 
   column sep=-\pgflinewidth,% 
   row sep=1mm,% 
   nodes in empty cells,
   nodes={draw, fill=gray!20,%
    minimum width=.5cm, outer sep=0pt,% 
    minimum height=.7cm, anchor=center}, 
   column 1/.style={nodes={minimum height=.8cm}},
   every label/.append style={name={}}]%
{ 
 |[label=left:1]| &[2mm] 2 & &[2mm] 5 & /  \\ 
 |[label=left:2]|& 1 & & 5 & &[2mm] 3 & &[2mm] 4 & / \\ 
 |[label=left:3]| & 2 & & 5 & & 3 & / & & \\ 
}; 

\foreach \i in {1,2,3}{ 
 %\path (M-\i-1) [late options={label=left:\i}]; 
 \draw[->] (M-\i-1)--(M-\i-2.west); 
 \draw[->] (M-\i-3.center)--(M-\i-4.west); 
} 

\draw[circarrow] (M-2-5.center)--(M-2-6.west); 
\draw[circarrow] (M-2-7.center)--(M-2-8.west); 
\draw[circarrow] (M-3-5.center)--(M-3-6.west); 

\end{scope}
\end{tikzpicture} 
\end{document}


Example of how label changes which node is named in the matrix (instead of expanding the node):

labels in matrices

\documentclass{standalone} 
\usepackage{tikz} 
\usetikzlibrary{matrix,fit} 


\begin{document} 
\begin{tikzpicture}
\matrix (M) [matrix of nodes,% 
   row sep=1mm,% 
   nodes={draw, fill=gray!20,%
    minimum width=.5cm, outer sep=0pt,% 
    minimum height=.7cm, anchor=center}
   ]%
{ 
 |[label=left:1]| &[2mm] 2\\
 |[label={[name=none]left:2}]|& 1\\
}; 

\foreach \i in {1,2}{ 
 \draw[->] (M-\i-1)--(M-\i-2.west);
 \node [fit=(M-\i-1),draw,red] {};
} 


\end{tikzpicture}