☰
【初阶数据结构】二叉树的概念
2026/10/22 7:25:07 网站建设 项目流程

摘要:本文是数据结构系列中关于树与二叉树的入门讲解。文章先介绍树的基本概念——一种非线性的层次结构,由根节点和互不相交的子树递归定义,并梳理了父节点、度、层次、高度等核心术语,以及「左孩子、右兄弟」的孩子兄弟表示法。随后重点讲解最常用的二叉树:每个节点最多两个孩子且左右有序,并区分了满二叉树(每层都满,2 K − 1 2^K-12K−1个节点)与完全二叉树(除最后一层外全满、最后一层从左到右连续)两种特殊形态。文章还推导了二叉树的关键性质(如n 0 = n 2 + 1 n_0=n_2+1n0​=n2​+1),并对比了顺序存储(数组,适合完全二叉树,下标可算父子关系)与链式存储(二叉链/三叉链,适合任意形状)两种存储结构的适用场景与取舍。

文章目录

    • 1. 树
      • 1.1 概念与结构
      • 1.2 相关术语
      • 1.3 树的表示
      • 1.4 树形结构的实际应用
    • 2. 二叉树
      • 2.1 概念与结构
      • 2.2 特殊的二叉树
        • 2.2.1 满二叉树
        • 2.2.2 完全二叉树
      • 2.3 二叉树的性质
      • 2.4 二叉树的存储结构
        • 2.4.1 顺序结构
        • 2.4.2 链式结构
      • 2.5 两种存储结构怎么选
    • 3. 小结

1. 树

1.1 概念与结构

前面五篇讲的都是线性表:元素一个挨一个排成一条线,每个元素最多一个前驱、一个后继。这一篇开始换一种数据结构——树。它是非线性的:一个节点可以有多个后继,于是元素之间不再是一条线,而是一层一层的层次关系。

树:由 n(n ≥ 0)个有限节点组成的一个具有层次关系的集合。它叫「树」,是因为画出来像一棵倒挂的树——根朝上,叶朝下。

  • 有一个特殊的节点,称为根节点,它没有前驱;
  • 除根节点外,其余节点被分成 M(M > 0)个互不相交的集合 T1、T2、……、Tm,其中每个集合又是一棵结构与树类似的子树。

第二条里藏着树最重要的特征:树是递归定义的——一棵树由根加上若干棵子树构成,而每棵子树本身又是树。后面讲遍历时会看到,树的几乎所有操作都是「先处理根,再对每棵子树做同样的事」。

对比项线性表树
元素关系一对一一对多
前驱 / 后继至多一个前驱、一个后继除根外有且仅有一个前驱,后继可以有多个
典型例子顺序表、链表、栈、队列文件系统、公司组织架构、DOM 树

树形结构里,子树之间不能有交集,否则就不是树形结构了——下面左图那种「两个父节点共用一个孩子」的情况,已经越出了树的定义:

三条关键性质

  • 子树是不相交的(一旦相交,就变成「图」了);
  • 除根节点外,每个节点有且仅有一个父节点;
  • 一棵有 N 个节点的树,恰好有N − 1 条边。

最后一条很好理解:除根以外的每个节点,都有且仅有一条边把它和父节点连起来。

1.2 相关术语

树的术语比较多,但都很好记——大部分借用了家谱里的说法:

术语含义图中示例
父节点 / 双亲节点含有子节点的节点,就是其子节点的父节点A 是 B 的父节点
子节点 / 孩子节点一个节点含有的子树的根节点B 是 A 的子节点
节点的度一个节点有几个孩子,度就是几A 的度为 6、F 的度为 2、K 的度为 0
树的度一棵树中最大的节点度图中树的度为 6
叶子节点 / 终端节点度为 0 的节点B、C、H、I、K… 都是叶节点
分支节点 / 非终端节点度不为 0 的节点D、E、F、G、J… 都是分支节点
兄弟节点具有相同父节点的节点B、C 是兄弟节点
节点的层次从根开始算,根为第 1 层,根的子节点为第 2 层,依此类推A 在第 1 层
树的高度 / 深度树中节点的最大层次图中树的高度为 4
节点的祖先从根到该节点所经分支上的所有节点A 是所有节点的祖先
路径从一个节点出发,沿「父 → 子」的连接到达另一个节点的序列A 到 Q 的路径:A-E-J-Q
子孙以某节点为根的子树中的任一节点所有节点都是 A 的子孙
森林m(m > 0)棵互不相交的树的集合——

几个容易混的点

  • 节点的度和树的度:前者是单个节点有几个孩子,后者是全树的最大值;
  • 层次从 1 开始数,所以「高度为 4」和「有 4 层」是同一件事;
  • 叶子只看「有没有孩子」,与它在第几层无关。

1.3 树的表示

线性表存起来很省心:一块连续空间(或一串节点)就是全部。树就麻烦了——既要存值,又要存节点之间的关系。常见的表示法有双亲表示法、孩子表示法、孩子双亲表示法、孩子兄弟表示法等,这里只介绍最常用的孩子兄弟表示法。

思路是「左孩子、右兄弟」:每个节点只保留两个指针,一个指向第一个孩子,一个指向右边的下一个兄弟。

typedefstructTreeNode{structTreeNode*child;// 左边开始的第一个孩子节点structTreeNode*brother;// 指向其右边的下一个兄弟节点intdata;// 节点中的数据域}TreeNode;

这样任意一棵树都能用二叉链表存下来:顺着child往下走是「进入下一层」,顺着brother往右走是「在同一层里找下一个兄弟」。

1.4 树形结构的实际应用

树不是抽象玩具,它就在你每天用的系统里。最典型的是文件系统:目录层层嵌套,正是一棵树。

除了文件系统,公司组织架构、网页的 DOM 树、编译器的语法树、数据库的索引,底层都是树形结构。

2. 二叉树

2.1 概念与结构

树的分支可以很多,研究起来太发散,于是我们限制一下:每个节点最多两个孩子。这就是用得最多的树形结构——二叉树。

二叉树:节点的有限集合,该集合或者为空,或者由一个根节点加上两棵分别称为左子树和右子树的二叉树组成。

这个定义本身就是递归的:二叉树 = 根 + 左子树(二叉树)+ 右子树(二叉树)。它有两条必须记住的特点:

  1. 二叉树不存在度大于 2的节点;
  2. 二叉树的子树有左右之分,次序不能颠倒——所以二叉树是有序树。

第 2 条经常被忽略:只有右孩子、和只有左孩子,是两棵不同的二叉树。

任意一棵二叉树,都是由下面五种基本形态复合出来的——写递归代码时,它们正好对应「空树返回」和「处理左右子树」两个分支:

2.2 特殊的二叉树

2.2.1 满二叉树

满二叉树:每一层的节点数都达到最大值。

换句话说,如果二叉树的层数为 K,节点总数是2 K − 1 2^K - 12K−1,它就是满二叉树——每一层都「塞满了」,没有任何空位。

2.2.2 完全二叉树

完全二叉树:深度为 K、有 n 个节点的二叉树,当且仅当它的每一个节点都与深度为 K 的满二叉树中编号从 1 至 n 的节点一一对应。

说人话就是:除了最后一层,上面每层都是满的;最后一层的节点从左到右连续排列,中间不能空。

完全二叉树的三条性质:

  1. 除了最后一层,每层节点数都达到最大;
  2. 最后一层的节点数不一定达到最大;
  3. 最后一层的节点从左到右依次排列,不允许中间缺位。

满二叉树是特殊的完全二叉树
满二叉树一定满足「除最后一层外全满、最后一层连续」;反过来不成立——完全二叉树允许最后一层不满。所以「满二叉树 ⊂ 完全二叉树」,这个包含关系在判断类题目里经常用到。

2.3 二叉树的性质

这些性质不用死记,理解「每层最多翻倍」就能推出来。下面的层次都规定根节点为第 1 层:

编号性质说明
1第i ii层最多有2 i − 1 2^{i-1}2i−1个节点第 1 层 1 个、第 2 层 2 个、第 3 层 4 个……每层翻倍
2深度为h hh的二叉树最多有2 h − 1 2^h - 12h−1个节点把每层的最大值加起来:1 + 2 + 4 + ⋯ + 2 h − 1 = 2 h − 1 1+2+4+\cdots+2^{h-1}=2^h-11+2+4+⋯+2h−1=2h−1
3有n nn个节点的满二叉树,深度h = log ⁡ 2 ( n + 1 ) h=\log_2(n+1)h=log2​(n+1)由性质 2 反解
4任意二叉树中,叶子数n 0 n_0n0​与度为 2 的节点数n 2 n_2n2​满足n 0 = n 2 + 1 n_0=n_2+1n0​=n2​+1见下面的推导
5有n nn个节点的完全二叉树,深度h = ⌊ log ⁡ 2 n ⌋ + 1 h=\lfloor \log_2 n\rfloor + 1h=⌊log2​n⌋+1完全二叉树最后一层可能不满,所以要向下取整

第 4 条推导只用两个等式——按节点数和按边数各数一遍:

设度为 0、1、2 的节点数分别是n 0 n_0n0​、n 1 n_1n1​、n 2 n_2n2​。

  • 按节点数:n = n 0 + n 1 + n 2 n = n_0 + n_1 + n_2n=n0​+n1​+n2​;
  • 按边数:树的边数一定是n − 1 n-1n−1(1.1 节的性质);另一方面,每条边都从某个节点「向下」连到自己的孩子,度为 1 的节点贡献 1 条边、度为 2 的贡献 2 条,所以边数= n 1 + 2 n 2 = n_1 + 2n_2=n1​+2n2​。

两式联立:

n 0 + n 1 + n 2 − 1 = n 1 + 2 n 2 ⟹ n 0 = n 2 + 1 n_0+n_1+n_2-1=n_1+2n_2 \;\Longrightarrow\; n_0=n_2+1n0​+n1​+n2​−1=n1​+2n2​⟹n0​=n2​+1

图中的例子两种数法都对得上:2 × 3 + 1 = 7 2\times3+1=72×3+1=7,而3 + 1 + 4 − 1 = 7 3+1+4-1=73+1+4−1=7。只要知道n 2 n_2n2​(或者知道总节点数),n 0 n_0n0​就能直接算出来。

2.4 二叉树的存储结构

二叉树一般可以用两种结构存储:顺序结构和链式结构。

2.4.1 顺序结构

顺序结构就是用数组存:把节点按「完全二叉树」的层序依次放进数组,根放在下标 0,然后一层一层、从左到右排下去。

因为数组下标是算出来的,父子关系不需要额外存——按下标就能直接跳到孩子或双亲(图中根在下标 0):

想找下标公式说明
左孩子2 i + 1 2i+12i+1下标i ii的节点,左孩子在下标2 i + 1 2i+12i+1
右孩子2 i + 2 2i+22i+2右孩子紧挨着左孩子
父节点⌊ ( i − 1 ) / 2 ⌋ \lfloor (i-1)/2 \rfloor⌊(i−1)/2⌋i = 0 i=0i=0时没有父节点(它是根)

两套编号,别混用
上面的公式是「根在下标 0」这一套;如果按「根编号为 1」来记,关系式会变成孩子2 i 2i2i、2 i + 1 2i+12i+1,父节点⌊ i / 2 ⌋ \lfloor i/2 \rfloor⌊i/2⌋。两套只差一个偏移量,同一段代码里只能用一套,否则算出来的下标会整体错位。

顺序存储的代价在空间上:数组必须按「满二叉树的编号」预留位置,所以它只适合完全二叉树。一旦树不完整,中间就会留下大量空位:

图中的树只有 6 个节点,却占了 10 个位置,空了下标 3、6、7、8 四处。更极端的情况是「只有右孩子的斜树」:h hh层的树只有h hh个节点,却要2 h − 1 2^h-12h−1个位置——指数级的浪费。

「堆」就是顺序存储的二叉树
现实中我们通常把堆(一种特殊的完全二叉树)用顺序结构的数组来存储,这也正是顺序结构最主流的用途。注意别和操作系统里那个「堆」混了:一个是数据结构,一个是管理内存的一块区域,只是名字撞了。

2.4.2 链式结构

链式结构用链表表示一棵二叉树:每个节点由三个域组成——数据域 + 左指针域 + 右指针域,两个指针分别指向左孩子和右孩子所在的节点。

链式结构又分二叉链和三叉链:二叉链只有左右两个指针,三叉链再多一个指向双亲的指针(后面的红黑树等结构会用到)。

三叉链多出来的「双亲指针」,换来的是「向上走」的能力:二叉链里想找父节点只能从根重新遍历,三叉链可以直接沿着双亲指针上去。代价是每个节点多一个指针,维护时也要多改一处。

2.5 两种存储结构怎么选

对比项顺序结构(数组)链式结构(二叉链)
存什么形状的树只适合完全二叉树,否则大量空位任意形状,空位不占空间
找孩子 / 找双亲下标计算,O ( 1 ) O(1)O(1)孩子O ( 1 ) O(1)O(1);双亲要遍历(或改用三叉链)
插入 / 删除要搬移或留标记,很别扭改指针即可,灵活
额外开销非完全二叉树可能有指数级浪费每个节点两个指针
典型用途堆、线段树、并查集(形状都是完全二叉树)一般二叉树、二叉搜索树、AVL / 红黑树

一句话:形状「满」就用数组,形状「随意」就用链表。

3. 小结

  • 树是非线性的层次结构:一个根、互不相交的子树,递归定义;N 个节点恰好 N − 1 条边。
  • 术语里最容易混的是节点的度 vs 树的度、层次 vs 高度;记牢「除根外每个节点只有一个父节点」这条,很多判断题就能反推。
  • 二叉树是最常用的树:每个节点最多两个孩子,且左右有序,不是「有孩子就行」。
  • 满二叉树每一层都满(2 K − 1 2^K-12K−1个节点);完全二叉树只要求「除最后一层外全满、最后一层从左到右连续」,满二叉树是它的特例。
  • 五个性质里最常考n 0 = n 2 + 1 n_0=n_2+1n0​=n2​+1,推导靠「按节点数」和「按边数」两个等式联立。
  • 顺序存储用数组按层序放节点,下标就能算出父子关系(根在 0:左2 i + 1 2i+12i+1、右2 i + 2 2i+22i+2、父⌊ ( i − 1 ) / 2 ⌋ \lfloor (i-1)/2\rfloor⌊(i−1)/2⌋),但只适合完全二叉树;链式存储(二叉链 / 三叉链)能表示任意形状,代价是每个节点两个指针。
  • 接下来会有两篇文章分别实现顺序结构二叉树(堆)与链式结构二叉树。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询