二叉树基础详解:TreeNode、buildTree、deleteTree 与 printTree 的实现原理(C++)

在 LeetCode、算法竞赛以及实际工程开发中,二叉树(Binary Tree)是最核心的数据结构之一。

很多初学者在刷题时,往往只会"调用"二叉树,却不真正理解:

  • TreeNode 为什么这样设计?

  • buildTree 是如何构造树的?

  • 为什么需要 deleteTree?

  • 如何优雅地打印一棵树?

本文将通过一个完整的 C++ 示例,系统讲解二叉树最核心的几个基础模块。


一、什么是二叉树(Binary Tree)

二叉树是一种:

每个节点最多只有两个子节点的数据结构。

通常:

  • 左孩子:left

  • 右孩子:right

例如:

复制代码
        3
       / \
      9  20
         / \
        15  7

这就是一棵典型二叉树。


二、TreeNode 节点结构

代码:

复制代码
struct TreeNode {
    int val;
    TreeNode *left;
    TreeNode *right;

    TreeNode() : val(0), left(nullptr), right(nullptr) {}

    TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}

    TreeNode(int x, TreeNode *left, TreeNode *right)
        : val(x), left(left), right(right) {}
};

三、TreeNode 的核心组成

一个二叉树节点通常包含三部分:

成员 含义
val 当前节点值
left 指向左子树
right 指向右子树

即:

复制代码
TreeNode
 ├── val
 ├── left
 └── right

四、为什么 left 和 right 是指针?

因为二叉树本质上是:

"节点之间的连接关系"

例如:

复制代码
    1
   / \
  2   3

节点 1 需要"指向"节点 2 和节点 3。

因此:

复制代码
TreeNode* left;
TreeNode* right;

用于保存子节点地址。


五、构造函数详解

1. 默认构造

复制代码
TreeNode() : val(0), left(nullptr), right(nullptr) {}

生成:

复制代码
值为0
左右孩子为空

2. 单参数构造

复制代码
TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}

例如:

复制代码
TreeNode* node = new TreeNode(5);

结果:

复制代码
节点值 = 5
left = nullptr
right = nullptr

3. 完整构造

复制代码
TreeNode(int x, TreeNode *left, TreeNode *right)

允许直接创建:

复制代码
根节点 + 左子树 + 右子树

例如:

复制代码
TreeNode* root =
    new TreeNode(1,
        new TreeNode(2),
        new TreeNode(3));

得到:

复制代码
    1
   / \
  2   3

六、buildTree:如何构建二叉树

代码:

复制代码
TreeNode* buildTree(vector<int> nodes)

作用:

根据层序数组构建二叉树。

例如:

复制代码
[3,9,20,null,null,15,7]

对应:

复制代码
        3
       / \
      9  20
         / \
        15  7

七、为什么使用队列 queue?

因为构建过程本质是:

层序遍历(Level Order Traversal)

需要:

复制代码
先处理父节点
再处理子节点

这是典型 BFS(广度优先搜索)思想。

因此:

复制代码
queue<TreeNode*> q;

用于保存"等待处理"的节点。


八、buildTree 核心流程

Step 1:创建根节点

复制代码
TreeNode* root = new TreeNode(nodes[0]);

Step 2:根节点入队

复制代码
q.push(root);

Step 3:循环处理队列

复制代码
while (!q.empty())

每次取出一个父节点:

复制代码
TreeNode* curr = q.front();
q.pop();

Step 4:构建左孩子

复制代码
curr->left = new TreeNode(nodes[i]);

Step 5:构建右孩子

复制代码
curr->right = new TreeNode(nodes[i]);

九、为什么用 -101 表示 null?

因为:

复制代码
vector<int>

不能直接存储:

复制代码
null

所以使用特殊值:

复制代码
-101

表示空节点。

例如:

复制代码
[1, -101, 2]

表示:

复制代码
    1
     \
      2

十、deleteTree:为什么必须释放内存?

代码:

复制代码
void deleteTree(TreeNode* root)

作用:

递归释放整棵树。

因为:

复制代码
new TreeNode(...)

是在堆区申请内存。

如果不释放:

复制代码
会造成内存泄漏(Memory Leak)

十一、deleteTree 为什么使用后序遍历?

代码:

复制代码
deleteTree(root->left);
deleteTree(root->right);
delete root;

顺序:

复制代码
左 -> 右 -> 根

原因:

必须先删除子节点,再删除父节点。

否则:

复制代码
父节点被释放后
无法继续访问孩子节点

十二、printTree:打印二叉树

很多时候调试二叉树非常困难。

因此我们通常会实现:

复制代码
printTree(root)

这里给出一个经典层序打印。


printTree 实现

复制代码
void printTree(TreeNode* root) {
    if (root == nullptr) {
        cout << "Empty Tree" << endl;
        return;
    }

    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty()) {
        int size = q.size();

        for (int i = 0; i < size; i++) {
            TreeNode* curr = q.front();
            q.pop();

            if (curr == nullptr) {
                cout << "null ";
                continue;
            }

            cout << curr->val << " ";

            q.push(curr->left);
            q.push(curr->right);
        }

        cout << endl;
    }
}

十三、printTree 输出效果

例如:

复制代码
vector<int> nodes = {3,9,20,-101,-101,15,7};

打印:

复制代码
3
9 20
null null 15 7

树结构一目了然。


十四、maxDepth:求二叉树最大深度

代码:

复制代码
int maxDepth(TreeNode* root)

核心思想:

复制代码
当前树高度 =
max(左子树高度, 右子树高度) + 1

即:

复制代码
return max(leftDepth, rightDepth) + 1;

十五、递归思想分析

例如:

复制代码
        3
       / \
      9  20
         / \
        15  7

递归会不断向下:

复制代码
3
├── 9
└── 20
    ├── 15
    └── 7

直到:

复制代码
root == nullptr

返回:

复制代码
0

然后逐层回溯计算高度。


十六、时间复杂度分析

buildTree

每个节点访问一次:

复制代码
O(n)

deleteTree

每个节点删除一次:

复制代码
O(n)

maxDepth

每个节点遍历一次:

复制代码
O(n)

复制代码
#include <iostream>
#include <vector>
#include <queue>
#include <stack>
#include <algorithm>

using namespace std;

/*
=========================================================
                    TreeNode Definition
=========================================================
*/

struct TreeNode {
    int val;
    TreeNode* left;
    TreeNode* right;

    TreeNode()
        : val(0), left(nullptr), right(nullptr) {}

    TreeNode(int x)
        : val(x), left(nullptr), right(nullptr) {}

    TreeNode(int x, TreeNode* left, TreeNode* right)
        : val(x), left(left), right(right) {}
};

/*
=========================================================
                    Build Binary Tree
            Level Order Construction
=========================================================
*/

TreeNode* buildTree(const vector<int>& nodes, int nullVal = -101)
{
    if (nodes.empty() || nodes[0] == nullVal) {
        return nullptr;
    }

    TreeNode* root = new TreeNode(nodes[0]);

    queue<TreeNode*> q;
    q.push(root);

    int i = 1;

    while (!q.empty() && i < nodes.size())
    {
        TreeNode* curr = q.front();
        q.pop();

        // left child
        if (i < nodes.size() && nodes[i] != nullVal)
        {
            curr->left = new TreeNode(nodes[i]);
            q.push(curr->left);
        }

        i++;

        // right child
        if (i < nodes.size() && nodes[i] != nullVal)
        {
            curr->right = new TreeNode(nodes[i]);
            q.push(curr->right);
        }

        i++;
    }

    return root;
}

/*
=========================================================
                    Delete Binary Tree
                Postorder Traversal
=========================================================
*/

void deleteTree(TreeNode* root)
{
    if (root == nullptr) {
        return;
    }

    deleteTree(root->left);
    deleteTree(root->right);

    delete root;
}

/*
=========================================================
                    Print Binary Tree
                Level Order Traversal
=========================================================
*/

void printTree(TreeNode* root)
{
    if (root == nullptr)
    {
        cout << "Empty Tree" << endl;
        return;
    }

    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty())
    {
        int size = q.size();

        for (int i = 0; i < size; i++)
        {
            TreeNode* curr = q.front();
            q.pop();

            if (curr == nullptr)
            {
                cout << "null ";
                continue;
            }

            cout << curr->val << " ";

            q.push(curr->left);
            q.push(curr->right);
        }

        cout << endl;
    }
}

/*
=========================================================
                    DFS Traversal
=========================================================
*/

// preorder
void preorder(TreeNode* root)
{
    if (root == nullptr) {
        return;
    }

    cout << root->val << " ";

    preorder(root->left);
    preorder(root->right);
}

// inorder
void inorder(TreeNode* root)
{
    if (root == nullptr) {
        return;
    }

    inorder(root->left);

    cout << root->val << " ";

    inorder(root->right);
}

// postorder
void postorder(TreeNode* root)
{
    if (root == nullptr) {
        return;
    }

    postorder(root->left);
    postorder(root->right);

    cout << root->val << " ";
}

/*
=========================================================
                    BFS Traversal
=========================================================
*/

void levelOrder(TreeNode* root)
{
    if (root == nullptr) {
        return;
    }

    queue<TreeNode*> q;
    q.push(root);

    while (!q.empty())
    {
        TreeNode* curr = q.front();
        q.pop();

        cout << curr->val << " ";

        if (curr->left)
            q.push(curr->left);

        if (curr->right)
            q.push(curr->right);
    }
}

/*
=========================================================
                    Tree Height
=========================================================
*/

int maxDepth(TreeNode* root)
{
    if (root == nullptr) {
        return 0;
    }

    return max(
        maxDepth(root->left),
        maxDepth(root->right)
    ) + 1;
}

/*
=========================================================
                    Count Nodes
=========================================================
*/

int countNodes(TreeNode* root)
{
    if (root == nullptr) {
        return 0;
    }

    return countNodes(root->left)
         + countNodes(root->right)
         + 1;
}

/*
=========================================================
                    Search Value
=========================================================
*/

bool search(TreeNode* root, int target)
{
    if (root == nullptr) {
        return false;
    }

    if (root->val == target) {
        return true;
    }

    return search(root->left, target)
        || search(root->right, target);
}

/*
=========================================================
                    Example Main
=========================================================
*/

int main()
{
    vector<int> nodes =
    {
        3,
        9,
        20,
        -101,
        -101,
        15,
        7
    };

    TreeNode* root = buildTree(nodes);

    cout << "Print Tree:" << endl;
    printTree(root);

    cout << endl;

    cout << "Preorder: ";
    preorder(root);
    cout << endl;

    cout << "Inorder: ";
    inorder(root);
    cout << endl;

    cout << "Postorder: ";
    postorder(root);
    cout << endl;

    cout << "LevelOrder: ";
    levelOrder(root);
    cout << endl;

    cout << "MaxDepth: ";
    cout << maxDepth(root) << endl;

    cout << "Node Count: ";
    cout << countNodes(root) << endl;

    cout << "Search 15: ";
    cout << search(root, 15) << endl;

    deleteTree(root);

    return 0;
}

十七、二叉树中的经典思想

这份代码实际上已经涵盖了:

思想 体现
BFS buildTree
DFS maxDepth
后序遍历 deleteTree
递归 maxDepth/deleteTree
队列 buildTree/printTree
指针 TreeNode

这也是 LeetCode 二叉树题目的核心基础。


十八、总结

本文系统讲解了:

  • TreeNode 节点设计

  • buildTree 层序建树

  • deleteTree 内存释放

  • printTree 层序打印

  • maxDepth 最大深度

理解这些内容后,你已经具备:

独立完成绝大多数二叉树基础题目的能力。

二叉树真正困难的部分并不是"写代码",而是:

  • 理解递归

  • 理解树结构

  • 理解 DFS 与 BFS

当你真正理解:

复制代码
树 = 递归结构

很多复杂题目都会变得清晰。

相关推荐
Severus_black2 小时前
【初阶数据结构与算法】八大排序之非比较排序(计数排序),一次性讲清!
数据结构·算法·排序算法
心之所向,自强不息2 小时前
# Unity MCP + Codex CLI 完整教程(Windows)
windows·unity·游戏引擎
特立独行的猫a2 小时前
Fast DDS Monitor Windows x64 从源码编译安装完全教程
windows·monitor·dds·fastdds·fastddsmonitor
罗西的思考2 小时前
【Agentic RL / 强化学习 / OPD】OpenClaw-RL 源码阅读笔记 --- (4)--- 系统架构
人工智能·算法·机器学习
QiLinkOS2 小时前
从技术到资产的跃迁:企业专利布局的深层逻辑
c语言·数据结构·c++·单片机·嵌入式硬件·算法·开源
aini_lovee2 小时前
FMCW雷达测速测距系统(锯齿波 + CFAR检测)
算法
qq_297574672 小时前
设计模式系列文章(基础篇第 11 篇):模板方法模式——定义算法骨架,实现代码复用与流程统一
算法·设计模式·模板方法模式
lqqjuly2 小时前
知识蒸馏:理论、算法与可运行实现
人工智能·深度学习·算法
水上冰石2 小时前
comfui的sd1.5模型,有多少采样算法,详解每一个采样算法
人工智能·算法