二叉树基础精讲(上):树形结构 · 二叉树概念 · 性质 · 遍历 · 基础操作全解析

🏠个人主页:得鹿梦鱼

🎬作者简介:C/C++/JAVA后端开发学习者

❄️个人专栏:C语言数据结构(C语言)EasyX

❄️个人专栏:JAVA SE数据结构与算法(JAVA)

❄️个人专栏:游戏规划程序人生

✨ 从来绝巘须孤往,万里同尘即玉京

文章目录

  • [二叉树基础精讲(上):树形结构 · 二叉树概念 · 性质 · 遍历 · 基础操作全解析](#二叉树基础精讲(上):树形结构 · 二叉树概念 · 性质 · 遍历 · 基础操作全解析)
    • [📝 文章摘要](#📝 文章摘要)
    • [🔍 前置知识回顾](#🔍 前置知识回顾)
  • 一、树型结构
  • 二、二叉树
    • [1. 概念](#1. 概念)
    • [2. 性质](#2. 性质)
    • [3. 遍历方式](#3. 遍历方式)
    • [4. 基本操作实现](#4. 基本操作实现)
      • [① 统计节点、叶子节点、第k层节点、树高度](#① 统计节点、叶子节点、第k层节点、树高度)
      • [② 查找指定值的节点](#② 查找指定值的节点)
      • [③ 层序遍历](#③ 层序遍历)
  • [🎯 核心考点总结](#🎯 核心考点总结)
  • [✍️ 写在最后](#✍️ 写在最后)

二叉树基础精讲(上):树形结构 · 二叉树概念 · 性质 · 遍历 · 基础操作全解析


📝 文章摘要

阅读时长 :25 分钟
适合人群

  1. 数据结构入门学习者 → 重点:树形结构概念、二叉树定义与性质
  2. 算法与笔试备考同学 → 重点:二叉树公式推导、完全二叉树高度计算
  3. Java 编程学习者 → 重点:二叉树节点定义、前中后序遍历
  4. 复习总结同学 → 重点:公式记忆、代码模板、递归思路

本文内容 :全覆盖树形结构二叉树概念满二叉树 & 完全二叉树5 大性质4 种遍历节点/叶子/高度/层序遍历等基础操作,从理论到代码,一次性打好二叉树基础。


🔍 前置知识回顾

学习本文前你需要掌握:

  1. 线性表、栈、队列基础结构
  2. 递归思想与简单递归代码
  3. Java 类、对象、引用
  4. 对数、等比数列等基础数学知识

一、树型结构

1. 概念

树是一种非线性结构,像一棵倒挂的树(根朝上,叶朝下)。

  • 树是递归定义的(树有根节点,根节点下有很多子树,子树根节点下又有很多子树)
  • 注意:
    ① 子树不相交,不能有环
    ② 除根节点外,每个节点有且仅有一个父结点
    ③ 一棵n个结点的树有n-1条边

2. 相关概念

  • 节点的度:一个节点含有子树的个数。如A的度为6
  • 叶子节点(终端节点):度为0的节点
  • 分支节点(非终端节点):度不为0的节点
  • 树的度:树中各节点的度的最大值
  • 根结点:一棵树中,没有双亲结点的结点。如上方树的根结点为A
  • 父结点(双亲节点):若一个节点含有子结点,这个节点是其子节点的父节点
  • 子节点(孩子节点):一个节点含有子树的根节点
  • 兄弟节点:具有相同父节点的节点
  • 节点的层次与树的高度(树的深度)
    • 推荐:根结点为第1层,根的子结点为第2层,依次类推。空树高度为0
    • 不推荐:根结点为第0层,根的子结点为第1层,依次类推。空树高度为-1
  • 节点的子孙:以某节点为根的子树任一结点
  • 节点的祖先:从根结点到该节点所经分支上的所有结点
  • 森林:由一棵或多棵互不相交的树的集合

3. 树的表示方法

(1)孩子兄弟表示法(左孩子右兄弟)

(对应示例树与链式存储结构)


二、二叉树

1. 概念

  • 任何一个二叉树可以分成三个部分:根节点、左子树、右子树
  • 二叉树有左右之分,其子树的次序不能颠倒
  • 有两种特殊的二叉树:满二叉树和完全二叉树

(1)满二叉树

一棵二叉树,如果每层的结点,都达到最大值(每层都是满的)

  • 假设一棵满二叉树高度为h,节点总个数为N,则:
    N = 2 h − 1 N = 2^h - 1 N=2h−1
    h = log ⁡ 2 ( N + 1 ) ≈ log ⁡ 2 N h = \log_2(N+1) \approx \log_2 N h=log2(N+1)≈log2N
    推导: 2 0 + 2 1 + ⋯ + 2 h − 1 = N 2^0 + 2^1 + \dots + 2^{h-1} = N 20+21+⋯+2h−1=N,由等比数列求和公式 2 0 ( 1 − 2 h ) 1 − 2 = 2 h − 1 = N \frac{2^0(1-2^h)}{1-2} = 2^h -1 = N 1−220(1−2h)=2h−1=N,得 h = log ⁡ 2 ( N + 1 ) h = \log_2(N+1) h=log2(N+1)

(2)完全二叉树

① 前h-1层都是满的

② 最后一层不满,但最后一层从左往右都是连续的

  • 假设一棵完全二叉树高度是h,节点总个数为N,则:
    h = log ⁡ 2 ( N + 1 + x ) ( 1 ≤ x ≤ 2 h − 1 − 1 ) h = \log_2(N+1+x) \quad (1 \le x \le 2^{h-1}-1) h=log2(N+1+x)(1≤x≤2h−1−1)
    且 log ⁡ 2 ( N + 2 ) ≤ h ≤ 1 + log ⁡ 2 N \log_2(N+2) \le h \le 1+\log_2 N log2(N+2)≤h≤1+log2N
    • 当 x = 1 x=1 x=1时, h = log ⁡ 2 ( N + 2 ) h = \log_2(N+2) h=log2(N+2)
    • 当 x = 2 h − 1 − 1 x=2^{h-1}-1 x=2h−1−1, h = log ⁡ 2 h = 1 + log ⁡ 2 N h = \log_2 h = 1+\log_2 N h=log2h=1+log2N

2. 性质

(1)若规定根节点的层数是1,则一棵非空二叉树第n层上最多有 2 n − 1 2^{n-1} 2n−1个结点

(2)若规定根节点的层数是1,则高度为h的二叉树最大结点为 2 h − 1 2^h -1 2h−1

(3)对任何一棵二叉树,如果其度为0的叶子结点个数为 n 0 n_0 n0,度为2的分支结点个数为 n 2 n_2 n2,则有 n 0 = n 2 + 1 \boldsymbol{n_0 = n_2 + 1} n0=n2+1

推导

设度为0的结点为 n 0 n_0 n0,度为1的结点为 n 1 n_1 n1,度为2的结点为 n 2 n_2 n2,总结点和为 N N N,总边和为 E E E,则
N = n 0 + n 1 + n 2 ① N = n_0 + n_1 + n_2 \quad ① N=n0+n1+n2①

由度为0的结点不会引出边,度为1的结点会引出一条边,度为2的结点会引出两条边,得
E = n 1 × 1 + n 2 × 2 + n 0 × 0    ⟹    E = n 1 + 2 n 2 ② E = n_1 \times 1 + n_2 \times 2 + n_0 \times 0 \implies E = n_1 + 2n_2 \quad ② E=n1×1+n2×2+n0×0⟹E=n1+2n2②

由除根节点外,每个结点都有一边指向自己,得
E = N − 1 ③ E = N - 1 \quad ③ E=N−1③

联立①②③:
{ N = n 0 + n 1 + n 2 ① E = n 1 + 2 n 2 ② E = N − 1 ③ \begin{cases} N = n_0 + n_1 + n_2 & ① \\ E = n_1 + 2n_2 & ② \\ E = N - 1 & ③ \end{cases} ⎩ ⎨ ⎧N=n0+n1+n2E=n1+2n2E=N−1①②③

得 n 0 + n 1 + n 2 − 1 = n 1 + 2 n 2 n_0 + n_1 + n_2 - 1 = n_1 + 2n_2 n0+n1+n2−1=n1+2n2

得 n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1,证毕。

(4)若规定根节点的层数是1,具有N个结点的满二叉树高度为h,则 h = log ⁡ 2 ( N + 1 ) h = \log_2(N+1) h=log2(N+1)(向上取整)

(5)对于具有n个结点的完全二叉树,如果按照由上至下、由左至右的顺序对所有节点从0开始编号,则对于序号为i的结点有:

  • leftchild = parent * 2 + 1
  • rightchild = parent * 2 + 2
  • parent = (child - 1) / 2(child为leftchild或rightchild)
    (连续的)

练习

一棵完全二叉树的结点为531,那么这棵树的高度为 10

假设这棵树的高度为h,最后一层缺了X个
2 h − 1 − x = 531 , x ∈ [ 0 , 2 h − 1 − 1 ] 2^h - 1 - x = 531, \quad x \in [0, 2^{h-1}-1] 2h−1−x=531,x∈[0,2h−1−1]

左边 ∈ [ 2 h − 1 , 2 h − 2 ] \in [2^{h-1}, 2^h - 2] ∈[2h−1,2h−2]
{ 2 9 = 512 2 10 = 1024 2 11 = 2048    ⟹    { h ≠ 9 , 531 ∉ [ 256 , 510 ] h = 10 , 531 ∈ [ 512 , 1022 ] h ≠ 11 , 531 ∉ [ 1024 , 2046 ] \begin{cases} 2^9 = 512 \\ 2^{10} = 1024 \\ 2^{11} = 2048 \end{cases} \implies \begin{cases} h \neq 9, 531 \notin [256, 510] \\ h = 10, 531 \in [512, 1022] \\ h \neq 11, 531 \notin [1024, 2046] \end{cases} ⎩ ⎨ ⎧29=512210=1024211=2048⟹⎩ ⎨ ⎧h=9,531∈/[256,510]h=10,531∈[512,1022]h=11,531∈/[1024,2046]

练习2

在具有2n个结点的完全二叉树中,叶子结点的个数为 n \boldsymbol{n} n

设度为0的结点为 n 0 n_0 n0,度为1的结点为 n 1 n_1 n1,度为2的结点为 n 2 n_2 n2,则
{ n 0 + n 1 + n 2 = 2 n n 0 = n 2 + 1    ⟹    2 n 0 + n 1 − 1 = 2 n    ⟹    n 0 = n + 1 − n 1 2 \begin{cases} n_0 + n_1 + n_2 = 2n \\ n_0 = n_2 + 1 \end{cases} \implies 2n_0 + n_1 - 1 = 2n \implies n_0 = n + \frac{1-n_1}{2} {n0+n1+n2=2nn0=n2+1⟹2n0+n1−1=2n⟹n0=n+21−n1
∵ n 1 = 0 \because n_1 = 0 ∵n1=0 或 1 1 1, ∴ n 0 = n + 1 2 \therefore n_0 = n + \frac{1}{2} ∴n0=n+21 或 n 0 = n n_0 = n n0=n

又 ∵ n 0 ∈ Z \because n_0 \in \mathbb{Z} ∵n0∈Z, ∴ n 0 = n \therefore n_0 = n ∴n0=n

(对应两种结构: n 1 = 1 n_1=1 n1=1 或 n 1 = 0 n_1=0 n1=0)


3. 遍历方式

(1)概述

  • 前序遍历(先根遍历):根 → 左子树 → 右子树
  • 中序遍历(中根遍历):左子树 → 根 → 右子树
  • 后序遍历(后根遍历):左子树 → 右子树 → 根
  • 层序遍历:一层一层遍历,核心思想:上层带下层

(2)代码实现

这段代码定义了二叉树最基础的节点结构 ,并实现了前、中、后三种递归遍历,是二叉树代码的核心模板。

java 复制代码
public class BinaryTree1 {
    static class TreeNode {
        public char val;        // 节点存储的值
        public TreeNode left;   // 左孩子引用
        public TreeNode right;  // 右孩子引用

        public TreeNode(char val) {
            this.val = val;
        }
    }

    // 前序遍历:先访问根,再访问左子树,最后访问右子树
    public void preOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        System.out.print(root.val + " ");
        preOrder(root.left);
        preOrder(root.right);
    }

    // 中序遍历:先访问左子树,再访问根,最后访问右子树
    public void inOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.val + " ");
        inOrder(root.right);
    }

    // 后序遍历:先访问左子树,再访问右子树,最后访问根
    public void postOrder(TreeNode root) {
        if (root == null) {
            return;
        }
        postOrder(root.left);
        postOrder(root.right);
        System.out.print(root.val + " ");
    }
}

4. 基本操作实现

这部分代码实现了二叉树最常用的基础功能,包括统计节点数、叶子数、树高度、查找节点、层序遍历。

① 统计节点、叶子节点、第k层节点、树高度

java 复制代码
public class BinaryTree2 {
    // 获取树中结点的个数(思路1:全局变量统计)
    public static int nodeSize = 0;
    public void size(TreeNode root) {
        if (root == null) {
            return;
        }
        nodeSize++;
        size(root.left);
        size(root.right);
    }

    // 获取树中结点个数(思路2:递归返回值统计,推荐)
    public int size2(TreeNode root) {
        if (root == null) {
            return 0;
        }
        // 左子树节点数 + 右子树节点数 + 自己
        return size2(root.left) + size2(root.right) + 1;
    }

    // 获取叶子节点的个数(思路1:全局变量统计)
    public static int leafSize;
    public void getLeafNodeCount(TreeNode root) {
        if (root == null) {
            return;
        }
        // 左右孩子都为空,就是叶子节点
        if (root.left == null && root.right == null) {
            leafSize++;
        }
        getLeafNodeCount(root.left);
        getLeafNodeCount(root.right);
    }

    // 获取叶子节点的个数(思路2:递归返回统计,推荐)
    public int getLeafNodeCount2(TreeNode root) {
        if (root == null) {
            return 0;
        }
        if (root.left == null && root.right == null) {
            return 1;
        }
        return getLeafNodeCount2(root.left) + getLeafNodeCount2(root.right);
    }

    // 获取第k层节点的个数
    public int getKLevelNodeCount(TreeNode root, int k) {
        if (root == null) {
            return 0;
        }
        // 递归到第一层,返回1
        if (k == 1) {
            return 1;
        }
        // 左右子树的第k-1层之和
        return getKLevelNodeCount(root.left, k-1) + getKLevelNodeCount(root.right, k-1);
    }

    // 获取二叉树的高度
    public int getHeight(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftHeight = getHeight(root.left);
        int rightHeight = getHeight(root.right);
        // 取左右子树最大高度 + 1(当前层)
        return leftHeight > rightHeight ? leftHeight + 1 : rightHeight + 1;
    }
}

② 查找指定值的节点

java 复制代码
// 判断值为value的元素是否存在,存在则返回该节点
public TreeNode findVal(TreeNode root, char val) {
    if (root == null) {
        return null;
    }
    // 当前节点就是目标节点
    if (root.val == val) {
        return root;
    }
    // 先去左子树查找
    TreeNode left = findVal(root.left, val);
    if (left != null) {
        return left;
    }
    // 左子树没找到,再去右子树查找
    TreeNode right = findVal(root.right, val);
    if (right != null) {
        return right;
    }
    // 都没找到
    return null;
}

③ 层序遍历

层序遍历使用队列实现,遵循"先进先出",一层一层遍历节点。

java 复制代码
// 层序遍历:从上到下、从左到右一层一层访问
public void levelOrder(TreeNode root) {
    if (root == null) {
        return;
    }
    Deque<TreeNode> deque = new LinkedList<>();
    TreeNode cur = root;
    deque.offer(root);
    while (!deque.isEmpty()) {
        cur = deque.poll();
        // 访问当前节点
        System.out.print(cur.val + " ");
        // 左孩子入队
        if (cur.left != null) {
            deque.offer(cur.left);
        }
        // 右孩子入队
        if (cur.right != null) {
            deque.offer(cur.right);
        }
    }
}

🎯 核心考点总结

  1. 树是递归结构,n 个节点 ⇒ n-1 条边
  2. 二叉树最多 两个孩子,严格区分左右
  3. 满二叉树:每层全满,节点数 2 h − 1 2^h-1 2h−1
  4. 完全二叉树:前 h-1 层满,最后一层靠左连续
  5. 必考公式: n 0 = n 2 + 1 n_0 = n_2 + 1 n0=n2+1
  6. 四种遍历:前、中、后、层序
  7. 高频操作:节点数、叶子数、高度、第 k 层、查找、层序

✍️ 写在最后

本文是二叉树系列第一篇 ,从最基础的树形结构讲起,完整覆盖概念、性质、公式、递归遍历、基础操作

这些内容是学习二叉树最核心的地基,也是面试、笔试、竞赛中出现频率极高的基础考点。

扎实掌握本文内容,后续学习平衡二叉树、堆、二叉搜索树等内容会更加轻松。

下一篇内容:
二叉树非递归遍历、翻转二叉树、对称二叉树、路径总和、层序遍历强化题

相关推荐
biwenjun9992 小时前
chatBI构建思路拆解(重点是元数据增强)
java·数据库·人工智能
Rsun045512 小时前
8、Java 代理模式从入门到实战
java·系统安全·代理模式
Q741_1472 小时前
每日一题 力扣 2515.到目标字符串的最短距离 循环数组 C++题解
c++·算法·leetcode
深邃-2 小时前
【C语言】-自定义类型:结构体
c语言·开发语言·数据结构·c++·html5
Dfreedom.2 小时前
聚类算法对比分析:K-Means、DBSCAN 与层次聚类
人工智能·算法·机器学习·kmeans·聚类
cmpxr_2 小时前
【C】结构体的内存对齐
c语言·开发语言·算法
人道领域2 小时前
【黑马点评日记02】Redis解决Tomcat集群Session共享问题
java·前端·后端·架构·tomcat·firefox
cheems95272 小时前
[JavaEE]深度解构 Spring 核心:从控制反转 (IoC) 到依赖注入 (DI) 的架构演进
java·spring·架构·java-ee
毅炼2 小时前
MySQL常见问题总结(2)
java·数据库·mysql