如何在 LaTeX 中画一个树状图(使用tikz和tikz-qtree包中的宏绘制树、森林、二叉树)

简单介绍

在计算机相关的文章中,树状图是最常见的几种图之一。树状图经常被用来用来演示结构、层次、算法等内容。而二叉树是最基础的树状图之一,掌握二叉树的画法就可以用图像展示一些算法或者数据结构了。

在 LaTeX 中,tikztikz-qtree两个包被用于绘制树状图,它们可以很方便的做出树状图来。。
tikz是 LaTeX 中一个用来绘制图的包,使用 Alexis Dimitriadis’ Qtree 语法。而tikz-qtree则是在tikz的基础上做出的一个专门绘制树状图的包,比如给 Qtree 增添了很多功能,以及在 pdfTEX 和 XƎTEX 上支持pst-qtree

2024年4月27日夜更新
在“节点样式”一节中,更新了节点属性的介绍,以及如何解决树节点外接圆重叠的问题。

TikZ

先来介绍一下如何在 TikZ 中绘制树状图。按照树、森林、二叉树的大致顺序介绍,因为二叉树比较简单,但是有足够展示各种使用方法。

如果你想查阅其他 TikZ 语法,可以查阅一个非官方的在线文档:[https://tikz.dev]。(https://tikz.dev)

代码结构

首先,LaTeX 文件的代码结构如下:

%自定义类型和格式
\documentclass[10pt]{article}
 %导入tikz包
\usepackage{tikz}
 
\begin{document}
 
\begin{tikzpicture}
    %在这里输入绘制树的代码
\end{tikzpicture}
 
\end{document}

下面演示的所有的代码,除非特殊说明,不然都是放在上述结构中的%在这里输入绘制树的代码的位置。

绘制一颗简单的树

绘制树的语句由表示树开始的\node 、节点信息和表示一颗树结束的分号;组成。大括号里写的是节点名称和信息。

比如下面这个语句会生成一颗有四个叶节点的树:

\node 
%这里的parent是要显示的文本内容
{parent}
	%这里的child表示是子节点,而后面的child 1才是要显示的文本内容
    child {node {child 1}}
    child {node {child 2}}
    child {node {child 3}}
    child {node {child 4}}
;

生成效果如下:
请添加图片描述

生成森林

上文说到,\node 表示树的开始,分号;表示树的结束。所以直接有几棵树写几段树的语句即可。只需要注意森林中每棵树的位置即可。

例如:

\node 
{parent}
    child {node {child 1}}
    child {node {child 2}}
    child {node {child 3}}
    child {node {child 4}}

;

\node at (0,2)
{z}
    child {node {a}}
    child {node {b}}
    child {node {3}}
    child {node {4}}
;

生成的森林如下:
请添加图片描述

绘制基础的二叉树

一棵基础的二叉树由一个父节点和两个字节点构成,生成代码如下:

\node 
{parent}
    child {node {child 1}}
    child {node {child 2}}
;

效果如下:
请添加图片描述

自定义某部分的颜色

如果想自定义颜色,在父节点类型{parent}前面或子节点类型名称child后面加个[],然后将颜色写在其中。

比如说用红色标记父节点:

\node 
[red]{parent}
    child {node {child 1}}
    child {node {child 2}}
;

效果如下:
请添加图片描述

再比如说用红色标记左子节点:

\node 
{parent}
    child [red]{node {child 1}}
    child {node {child 2}}
;

效果如下:
请添加图片描述

线的样式

可以使用下面代码来调整树中线段的样式,比如实线、虚线等。

默认情况下,线段是实线,但是如果要把上面这个树的右节点的线段从实线换成虚线,那么可以使用下面的语句来实现:

\node 
{parent}
    child [red] {node {child 1}}
    child {node {child 2} edge from parent [dashed]}
;

效果如下:
请添加图片描述
别看这里很简单,但是支持或可以实现的样式非常非常多,这里细说太冗余了,还请移步:https://tikz.dev/tikz-actions

节点样式

绘制内容简单的树状图的时候,一般都会用在节点外围画一个圆,那么可以在父节点类型{parent}前面或子节点类型名称child后面加个[],在其中加上draw和要形状等属性信息(如果有的话),从而来为前文绘制的二叉树添加圆圈。如下:

\node
[draw, circle] {parent}
    child {node[draw, circle] {child 1}}
    child {node[draw, circle] {child 2}}
;

效果如下:
请添加图片描述

2024年4月27日夜更新
由于我当时树节点用的文本比较短,所以没有考虑到内容尺寸较大会导致圆圈重叠的问题,感谢评论区有人指出。节点圆圈重叠如下(节点内容是乱打的):

\begin{tikzpicture}
\node [draw, circle] {pareakg,jgasjhgant}
    child {node[draw, circle] {childjasgfi1}}
    child {node[draw, circle] {childbafjskh,k2}}
;
\end{tikzpicture}

请添加图片描述

这是因为这个圆是节点的外接圆。而节点的结构如下(图源自How the inner separation for nodes works and how to use it: Part 2 - TikZ Blog),如果你了解一些前端知识应该会觉得这些很眼熟:

请添加图片描述

所以这个外接圆的大小取决于节点的大小。而节点的位置以及上图中除了content之外的属性,都是由\tikzset中的属性决定的。这就使得自动调整树的样式是有限度的,因为有些值会自动修改,而有的不会,这就需要手动改一下。

设置这些属性的值有两种方法:

  1. 在文章开头用\tikzset{属性}来设置整篇文章中所有 TikZ 生成的树的样式,比如\tikzset{level distance=72pt},这句代码设置了树的每一层之间的距离为72 pt
  2. 在每一个树的\begin{tikzpicture}后面使用方括号[]包裹属性,来设置当前树的属性。

一般使用第二个方法较多,这里也使用这种。

这里继续使用上面那个例子来解释一些属性的含义,但是首先需要知道要改什么属性:

  1. 对于层属性而言,需要改
    • sibling distance(一个父节点下的子节点之间的距离)
    • level distance(树中每一层之间的距离)
  2. 对于节点属性来说,需要改
    • align(布局),这里我们将其修改成center居中。
    • inner sep:它就是上图中padding的部分。如果你了解 CSS 等前端,他和那些前端中的padding是一样的。反之outer sep就是margin部分。这影响会影响外接圆的大小,我们将其设置为0。如果你想设置为其他数值,那么请注意minimum size的值需要大一些。
    • minimum size(节点的最小尺寸)。这里设置最小尺寸是因为,如果你设置最大值,那么如果内容大小超出了,会直接报错。这不如设置最小值,然后分行或者调整字号后再一步步调整至合适大小。

修改节点属性主要是为了每个节点可以一样大,不然只修改层属性就可以做到圆圈不重叠,就是有点不好看,哪怕内容使用两个反斜杠\\将内容分行了,也很容易一个大一个小。

更多节点属性及其介绍请见:17 Nodes and Edges - PGF/TikZ Manual

将代码改成(只修改了开头两行,先不对内容进行分行):

\begin{tikzpicture}[level/.style={sibling distance=72pt, level distance=72pt},
                    every node/.style={align=center, inner sep=0, minimum size=50pt}]
\node [draw, circle] {pareakg,jgasjhgant}
    child {node[draw, circle] {childjasgfi1}}
    child {node[draw, circle] {childbafjskh,k2}}
;
\end{tikzpicture}

生成树如下:

请添加图片描述

这时候有两种继续方案:

  1. 修改字体至合适大小:这个方案问题很多,比如因为排版限制了尺寸,如果字号太小很多时候会看不清。
  2. 对内容进行分行:这个方案可以保证字体大小,但是对一些内容分行可能会导致误解或误导。

这里使用两个反斜杠\\对内容分行,如何分行取决于实际情况和个人爱好:

\begin{tikzpicture}[level/.style={sibling distance=72pt, level distance=72pt},
                    every node/.style={align=center, inner sep=0, minimum size=55pt}]
\node [draw, circle] {pareakg,\\jgasjhgant}
    child {node[draw, circle] {childj\\asgfi1}}
    child {node[draw, circle] {childbaf\\jskh,k2}}
;
\end{tikzpicture}

生成树如下:

请添加图片描述

可以看到虽然我们设置了inner sep=0,但是两个子节点的内容周围是有空白的,这是因为我们设置了最小尺寸,而父节点就几乎没有。

如果你有多层树,如下:

\begin{tikzpicture}[level/.style={sibling distance=72pt, level distance=72pt},
                    every node/.style={align=center, inner sep=0, minimum size=55pt}]
\node [draw, circle] {pareakg,\\jgasjhgant}
    child {node[draw, circle] {childj\\asgfi1} child {node[draw, circle] {childj\\asgfi1}}
    child {node[draw, circle] {childbaf\\jskh,k2}}}
    child {node[draw, circle] {childj\\asgfi1} child {node[draw, circle] {childj\\asgfi1}}
    child {node[draw, circle] {childbaf\\jskh,k2}}}
;
\end{tikzpicture}

生成树如下:
请添加图片描述

可以看到有子节点又重叠了。这是由于sibling distance是设置每一个父节点的子节点之间的距离,包括根节点,那么做个小学数学题,会发现一定会有重叠的。对于这种情况,解决方案一般只用调大根节点的子节点之间的距离即可,不用一步步去调整每一层的。方法很简单,加上/#1即可表示只修改根节点的,如下:

\begin{tikzpicture}[level/.style={sibling distance=150pt/#1, level distance=72pt},
                    every node/.style={align=center, inner sep=0, minimum size=55pt}]
\node [draw, circle] {pareakg,\\jgasjhgant}
    child {node[draw, circle] {childj\\asgfi1} child {node[draw, circle] {childj\\asgfi1}}
    child {node[draw, circle] {childbaf\\jskh,k2}}}
    child {node[draw, circle] {childj\\asgfi1} child {node[draw, circle] {childj\\asgfi1}}
    child {node[draw, circle] {childbaf\\jskh,k2}}}
;
\end{tikzpicture}

这时候生成的树如下:

请添加图片描述

参考资料:
17 Nodes and Edges - PGF/TikZ Manual

tikz-qtree: better trees with TikZ - David Chiang

Some questions regarding tikz tree - TeX StackExchange

绘制复杂二叉树(嵌套使用)

二叉树不可能只有这种基础组成,所以还要来嵌套使用。而嵌套就是在某个子节点中(也就是大括号中{}),写它的子节点。看看下面的例子你就能懂了:

\node {parent}
    child {node {1}} 
    child {
      node {2}
        child {node {3}}
        child {node {4}}
    }
;

生成的二叉树效果如下:
请添加图片描述

tikz-qtree

可以看到,使用tikz包画复杂的树很很麻烦,要打一堆nodechild等词,所以tikz-qtree进行了改进,使用简单的语法即可画出复杂的树。

代码结构

首先,LaTeX 文件的代码结构如下:

%自定义类型和格式
\documentclass[10pt]{article}
 %导入tikz包和tikz-qtree包(因为后者是基于前者,所以必须两个都导入)
\usepackage{tikz}
\usepackage{tikz-qtree}
 
\begin{document}
 
\begin{tikzpicture}
    %在这里输入绘制树的代码
\end{tikzpicture}
 
\end{document}

下面演示的所有的代码,除非特殊说明,不然都是放在上述结构中的%在这里输入绘制树的代码的位置。

绘制一颗简单的树

tikz-qtree画一棵树非常简单。

树的开头使用\Tree表示开始。方括号[]用来划分出子树,句点.表示子树的根。语句如下(需要注意的是,子树的根节点后面就算没有节点了,但是也要加空格):

\Tree
[.1
[.2 ] [.3 ] [.4 ] [.5 ] 
]

生成树如下:
请添加图片描述

生成森林

tikz-qtree生成森林的方式和tikz不一样,因为一个tikzpicture作用域中只能有一颗树。所以要写在多个tikzpicture中。如果想水平摆放,可以直接连着写:

%自定义类型和格式
\documentclass[10pt]{article}
 %导入tikz包和tikz-qtree包(因为后者是基于前者,所以必须两个都导入)
\usepackage{tikz}
\usepackage{tikz-qtree}
 
\begin{document}
 
\begin{tikzpicture}
    \Tree
[.1
[.2 ] [.3 ] [.4 ] [.5 ] 
]
\end{tikzpicture}

\begin{tikzpicture}
    \Tree
[.1
[.2 ] [.3 ] [.4 ] [.5 ] 
]
\end{tikzpicture}
 
\end{document}

生成效果如下:
请添加图片描述

如果想指定树叶的方向和排列位置,可以在tikzpicture后面加上[grow=][grow'=]来指定,参数为updownleftright来表示开口向上下左右方向。
并且这两种参数不太一样,

  1. [grow=]是逆时针排列子节点;
  2. [grow'=]是顺时针排序子节点。

不仅是为了方便理解,还因为这个使用有点特殊。如果想两棵树并排,那需要在两个节点之间使用百分号%。下面来举几个例子来详细说明一下。

下面语句会生成上下排列、子节点逆时针排列的两个二叉树:

\begin{tikzpicture}[grow'=up]
\Tree
[.1
[.2 ] [.3 ]
]
\end{tikzpicture}

\begin{tikzpicture}[grow'=down]
\Tree
[.1
[.2 ] [.3 ]
]
\end{tikzpicture}

生成图如下:
请添加图片描述

但是给语句中间加上百分号%,那就会得到并列的树:

\begin{tikzpicture}[grow'=up]
\Tree
[.1
[.2 ] [.3 ]
]
\end{tikzpicture}
%
\begin{tikzpicture}[grow'=down]
\Tree
[.1
[.2 ] [.3 ]
]
\end{tikzpicture}

请添加图片描述

绘制基础的二叉树

上文已经看到了,绘制二叉树只用很简单的结构即可。.开头的表示跟节点,下面每个叶节点用方括号[]包裹起来:

\begin{tikzpicture}
\Tree
[.1
[.2 ] [.3 ]
]
\end{tikzpicture}

线的样式

没有那么丰富的自定义功能,但是可以支持调整线的粗细、样式等属性。

如果想加粗所有的线和节点,就使用以下语句:

\tikzset{edge from parent/.append style={very thick}}

例如:

\begin{tikzpicture}
\tikzset{edge from parent/.append style={very thick}}

\Tree
[.1
[.2 ] [.3 ]
]
\end{tikzpicture}

请添加图片描述

虚线的话将[dashed]加在\begin{tikzpicture}后面,如下:

\begin{tikzpicture}[dashed]
\Tree
[.1
[.2 ] [.3 ]
]
\end{tikzpicture}

请添加图片描述

如果想带箭头,那么将[semithick,->]加在\begin{tikzpicture}后面,如下:

\begin{tikzpicture}[semithick,->]
\Tree
[.1
[.2 ] [.3 ]
]
\end{tikzpicture}

请添加图片描述

节点样式

如何加粗节点上一节提到了,使用\tikzset{edge from parent/.append style={very thick}},那么如何给节点加圆圈呢?

只能一个个节点加圆圈,使用以下结构和语句:

\begin{tikzpicture}
\Tree
[.1
[.2 ] [.\node[draw, circle]{3}; ]
]
\end{tikzpicture}

请添加图片描述

绘制复杂二叉树(嵌套使用)

tikz-qtree来绘制复杂的二叉树就很容易了,就是需要注意大括号闭合。
中括号[]包含每个节点和其子节点。.表示根节点,后面跟的是其子节点。

\begin{tikzpicture}
\Tree
[.1
[.2 [.3 ] ] 
[.4 [5 6 ] ] 
]
\end{tikzpicture}

请添加图片描述

希望能帮到有需要的人~

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值