跳到主要内容
极客日志极客日志
首页博客AI提示词GitHub精选代理工具
搜索
|注册
博客列表
C算法

数据结构初阶:二叉树的链式存储结构

综述由AI生成二叉树链式存储结构通过节点指针连接左右子树,支持灵活扩展。内容涵盖节点创建、四种遍历方式(前序、中序、后序、层序)及统计操作(总节点、叶子节点、第 k 层、深度)。通过递归与队列配合,实现了完整的二叉树功能测试与验证。

GRACE Grace发布于 2026/3/27更新于 2026/5/15 浏览
数据结构初阶:二叉树的链式存储结构

一、二叉树的链式结构

有了顺序存储结构,自然也会想到链式存储。在二叉树的讲解中,我们提到过二叉链和三叉链,现阶段主要关注二叉链。

// 链式二叉树的节点
typedef struct BinaryTree {
    Elemtype data;
    struct BinaryTree* leftChild;
    struct BinaryTree* rightChild;
} BTNode;

每个节点包含数据域和指针域。数据域存储当前值,指针域分别指向左子树和右子树的地址,便于直接访问子节点。

二、二叉树的创建

为了快速上手并理解基本性质,我们先手动创建一棵二叉树,暂不涉及复杂的增删查改操作。

2.1、创建二叉树节点

与链表节点类似,分配内存并初始化指针。

// 创建新节点
BTNode* BuyNode(Elemtype data) {
    BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
    if (newNode == NULL) {
        perror("create new node fail!\n");
    }
    newNode->data = data;
    newNode->leftChild = newNode->rightChild = NULL;
    return newNode;
}

注意返回新节点的指针,否则后续无法访问。

2.2、二叉树节点的链接

通过指针将根节点与左右子树连接起来。

BTNode* createNewBT() {
    BTNode* NodeA = BuyNode('A');
    BTNode* NodeB = BuyNode('B');
    BTNode* NodeC = BuyNode('C');
    BTNode* NodeD = BuyNode('D');
    BTNode* NodeE = BuyNode('E');
    BTNode* NodeF = BuyNode('F');
    BTNode* NodeG = BuyNode('G');
    BTNode* NodeH = BuyNode('H');
    BTNode* NodeI = BuyNode('I');
    BTNode* NodeO = BuyNode();

    NodeA->leftChild = NodeB;
    NodeA->rightChild = NodeC;
    NodeB->leftChild = NodeD;
    NodeB->rightChild = NodeE;
    NodeD->leftChild = NodeH;
    NodeC->leftChild = NodeF;
    NodeF->leftChild = NodeI;
    NodeH->leftChild = NodeO;

     NodeA;
}
'O'
return

构建出的树结构如下所示:

文章配图

三、链式二叉树的基本操作

主要涉及遍历、统计及查找等操作。

3.1、前序遍历

访问顺序:根 -> 左 -> 右。

void PreOrder(BTNode* root) {
    if (root == NULL) {
        printf("NULL ");
        return;
    }
    printf("%c ", root->data);
    PreOrder(root->leftChild);
    PreOrder(root->rightChild);
}

递归终止条件是节点为空。以叶子节点 O 为例,其左右孩子均为 NULL,递归返回后打印自身,整棵树遵循'根左右'原则执行。

3.2、中序遍历

访问顺序:左 -> 根 -> 右。

void InOrder(BTNode* root) {
    if (root == NULL) {
        printf("NULL ");
        return;
    }
    InOrder(root->leftChild);
    printf("%c ", root->data);
    InOrder(root->rightChild);
}

先彻底遍历左子树,再访问根节点,最后遍历右子树。

3.3、后序遍历

访问顺序:左 -> 右 -> 根。

void LastOrder(BTNode* root) {
    if (root == NULL) {
        printf("NULL ");
        return;
    }
    LastOrder(root->leftChild);
    LastOrder(root->rightChild);
    printf("%c ", root->data);
}

左右子树全部遍历完毕后,才访问根节点。

3.4、计算二叉树的总结点个数

采用递归方式,统计左右子树节点数之和。

int BinaryTreeSize_of_Node(BTNode* root) {
    if (root == NULL) {
        return 0;
    }
    return 1 + BinaryTreeSize_of_Node(root->leftChild) + BinaryTreeSize_of_Node(root->rightChild);
}
3.5、计算二叉树的叶子结点个数

叶子节点定义为左右孩子均为空的节点。

int BinaryTreesize_of_Leaf(BTNode* root) {
    if (root == NULL) return 0;
    if (root->leftChild == NULL && root->rightChild == NULL) return 1;
    return BinaryTreesize_of_Leaf(root->leftChild) + BinaryTreesize_of_Leaf(root->rightChild);
}
3.6、计算第 k 层节点个数

递归统计左子树第 k-1 层与右子树第 k-1 层的节点数之和。

int BinaryTreeSize_of_K(BTNode* root, int k) {
    if (root == NULL) return 0;
    if (k == 1) return 1;
    return BinaryTreeSize_of_K(root->leftChild, k - 1) + BinaryTreeSize_of_K(root->rightChild, k - 1);
}

当 k=1 时,无论输入还是递归到达,该层只有一个根节点。

3.7、计算二叉树的深度

比较左右子树的最大深度,加 1 即为当前树深。

int BinaryTreeSize_of_Depth(BTNode* root) {
    if (root == NULL) {
        return 0;
    }
    int leftDepth = BinaryTreeSize_of_Depth(root->leftChild);
    int rightDepth = BinaryTreeSize_of_Depth(root->rightChild);
    return 1 + (leftDepth > rightDepth ? leftDepth : rightDepth);
}
3.8、查找元素

遍历整棵树,匹配目标值。

void BinaryTreeSearch_of_val(BTNode* root, Elemtype val) {
    if (root == NULL) return;
    if (root->data == val) {
        printf("找到了!\n");
        return;
    }
    BinaryTreeSearch_of_val(root->leftChild, val);
    BinaryTreeSearch_of_val(root->rightChild, val);
}
3.9、层序遍历

借助队列实现,按层级从左到右访问。

void LevelOrder(BTNode* root) {
    Queue q;
    Queue* pQueue = &q;
    InitQueue(pQueue);
    Push_Queue(pQueue, root);
    while (!isEmpty(pQueue)) {
        BTNode* top = GetElemFront(pQueue);
        printf("%c ", top->data);
        Pop_Queue(pQueue);
        if (top->leftChild) {
            Push_Queue(pQueue, top->leftChild);
        }
        if (top->rightChild) {
            Push_Queue(pQueue, top->rightChild);
        }
    }
    DestroyQueue(pQueue);
}
3.10、判断是否为完全二叉树

利用层序遍历,遇到空节点后,后续节点必须全为空。

bool IsBinaryTree_of_Complete(BTNode* root) {
    Queue q;
    Queue* pQueue = &q;
    InitQueue(pQueue);
    Push_Queue(pQueue, root);
    while (!isEmpty(pQueue)) {
        QElemtype top = GetElemFront(pQueue);
        Pop_Queue(pQueue);
        if (top == NULL) {
            break;
        }
        Push_Queue(pQueue, top->leftChild);
        Push_Queue(pQueue, top->rightChild);
    }
    while (!isEmpty(pQueue)) {
        BTNode* top = GetElemFront(pQueue);
        Pop_Queue(pQueue);
        if (top != NULL) {
            DestroyQueue(pQueue);
            return false;
        }
    }
    DestroyQueue(pQueue);
    return true;
}
3.11、外部代码的引入

需将队列相关代码(如 Queue.h)引入项目。注意类型定义的一致性,队列元素类型应调整为 struct BinaryTree*。

四、综合代码

整合上述功能进行测试。

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include"Queue.h"

typedef char Elemtype;

typedef struct BinaryTree {
    Elemtype data;
    struct BinaryTree* leftChild;
    struct BinaryTree* rightChild;
} BTNode;

BTNode* BuyNode(Elemtype data) {
    BTNode* newNode = (BTNode*)malloc(sizeof(BTNode));
    if (newNode == NULL) {
        perror("create new node fail!\n");
    }
    newNode->data = data;
    newNode->leftChild = newNode->rightChild = NULL;
    return newNode;
}

BTNode* createNewBT() {
    BTNode* NodeA = BuyNode('A');
    BTNode* NodeB = BuyNode('B');
    BTNode* NodeC = BuyNode('C');
    BTNode* NodeD = BuyNode('D');
    BTNode* NodeE = BuyNode('E');
    BTNode* NodeF = BuyNode('F');
    BTNode* NodeG = BuyNode('G');
    BTNode* NodeH = BuyNode('H');
    BTNode* NodeI = BuyNode('I');
    BTNode* NodeO = BuyNode('O');
    NodeA->leftChild = NodeB;
    NodeA->rightChild = NodeC;
    NodeB->leftChild = NodeD;
    NodeB->rightChild = NodeE;
    NodeD->leftChild = NodeH;
    NodeC->leftChild = NodeF;
    NodeF->leftChild = NodeI;
    NodeH->leftChild = NodeO;
    return NodeA;
}

void PreOrder(BTNode* root) {
    if (root == NULL) { printf("NULL "); return; }
    printf("%c ", root->data);
    PreOrder(root->leftChild);
    PreOrder(root->rightChild);
}

void InOrder(BTNode* root) {
    if (root == NULL) { printf("NULL "); return; }
    InOrder(root->leftChild);
    printf("%c ", root->data);
    InOrder(root->rightChild);
}

void LastOrder(BTNode* root) {
    if (root == NULL) { printf("NULL "); return; }
    LastOrder(root->leftChild);
    LastOrder(root->rightChild);
    printf("%c ", root->data);
}

int BinaryTreeSize_of_Node(BTNode* root) {
    if (root == NULL) { return 0; }
    return 1 + BinaryTreeSize_of_Node(root->leftChild) + BinaryTreeSize_of_Node(root->rightChild);
}

int BinaryTreesize_of_Leaf(BTNode* root) {
    if (root == NULL) { return 0; }
    if (root->leftChild == NULL && root->rightChild == NULL) { return 1; }
    return BinaryTreesize_of_Leaf(root->rightChild) + BinaryTreesize_of_Leaf(root->leftChild);
}

int BinaryTreeSize_of_K(BTNode* root, int k) {
    if (root == NULL) { return 0; }
    if (k == 1) { return 1; }
    else { return BinaryTreeSize_of_K(root->rightChild, k - 1) + BinaryTreeSize_of_K(root->leftChild, k - 1); }
}

int BinaryTreeSize_of_Depth(BTNode* root) {
    if (root == NULL) { return 0; }
    int leftDepth = BinaryTreeSize_of_Depth(root->leftChild);
    int rightDepth = BinaryTreeSize_of_Depth(root->rightChild);
    return 1 + (leftDepth > rightDepth ? leftDepth : rightDepth);
}

void BinaryTreeSearch_of_val(BTNode* root, Elemtype val) {
    if (root == NULL) { return; }
    if (root->data == val) { printf("找到了!\n"); return; }
    BinaryTreeSearch_of_val(root->leftChild, val);
    BinaryTreeSearch_of_val(root->rightChild, val);
}

void LevelOrder(BTNode* root) {
    Queue q;
    Queue* pQueue = &q;
    InitQueue(pQueue);
    Push_Queue(pQueue, root);
    while (!isEmpty(pQueue)) {
        BTNode* top = GetElemFront(pQueue);
        printf("%c ", top->data);
        Pop_Queue(pQueue);
        if (top->leftChild) { Push_Queue(pQueue, top->leftChild); }
        if (top->rightChild) { Push_Queue(pQueue, top->rightChild); }
    }
    DestroyQueue(pQueue);
}

bool IsBinaryTree_of_Complete(BTNode* root) {
    Queue q;
    Queue* pQueue = &q;
    InitQueue(pQueue);
    Push_Queue(pQueue, root);
    while (!isEmpty(pQueue)) {
        QElemtype top = GetElemFront(pQueue);
        Pop_Queue(pQueue);
        if (top == NULL) { break; }
        Push_Queue(pQueue, top->leftChild);
        Push_Queue(pQueue, top->rightChild);
    }
    while (!isEmpty(pQueue)) {
        BTNode* top = GetElemFront(pQueue);
        Pop_Queue(pQueue);
        if (top != NULL) {
            DestroyQueue(pQueue);
            return false;
        }
    }
    DestroyQueue(pQueue);
    return true;
}

int main() {
    BTNode* NodeA = createNewBT();
    printf("前序遍历:"); PreOrder(NodeA); printf("\n\n");
    printf("中序遍历:"); InOrder(NodeA); printf("\n\n");
    printf("后序遍历:"); LastOrder(NodeA); printf("\n\n");
    int size_of_totalNode = BinaryTreeSize_of_Node(NodeA);
    printf("当前二叉树的总结点个数为:%d", size_of_totalNode); printf("\n\n");
    int size_of_LeafNode = BinaryTreesize_of_Leaf(NodeA);
    printf("当前二叉树的叶子结点个数为:%d", size_of_LeafNode); printf("\n\n");
    int size_of_KNode = BinaryTreeSize_of_K(NodeA, 4);
    printf("当前二叉树的第 4 层的结点个数为:%d", size_of_KNode); printf("\n\n");
    int size_of_Depth = BinaryTreeSize_of_Depth(NodeA);
    printf("当前二叉树的层数为:%d", size_of_Depth); printf("\n\n");
    BinaryTreeSearch_of_val(NodeA, 'I');
    printf("层序遍历的结果为:"); LevelOrder(NodeA); printf("\n\n");
    if (IsBinaryTree_of_Complete(NodeA)) {
        printf("一棵完全二叉树");
    } else {
        printf("这不是完全二叉树...\n");
    }
    return 0;
}

对应的队列实现文件 Queue.h 需包含基础入队出队逻辑,注意类型重定义。

运行结果验证了各功能的正确性,整体结构清晰,便于理解二叉树的核心操作逻辑。

目录

  1. 一、二叉树的链式结构
  2. 二、二叉树的创建
  3. 2.1、创建二叉树节点
  4. 2.2、二叉树节点的链接
  5. 三、链式二叉树的基本操作
  6. 3.1、前序遍历
  7. 3.2、中序遍历
  8. 3.3、后序遍历
  9. 3.4、计算二叉树的总结点个数
  10. 3.5、计算二叉树的叶子结点个数
  11. 3.6、计算第 k 层节点个数
  12. 3.7、计算二叉树的深度
  13. 3.8、查找元素
  14. 3.9、层序遍历
  15. 3.10、判断是否为完全二叉树
  16. 3.11、外部代码的引入
  17. 四、综合代码
  • 💰 8折买阿里云服务器限时8折了解详情
  • GPT-5.5 超高智商模型1元抵1刀ChatGPT中转购买
  • 代充Chatgpt Plus/pro 帐号了解详情
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Ubuntu 24.04 安装 OpenClaw 时 systemctl is-enabled 报错解决
  • Flutter 使用 wasm_ffi 在鸿蒙端调用 WebAssembly 实战
  • C++ 竞赛代码风格规范建议
  • 无人机“黑飞”正式入法:2026 年 1 月 1 日起违规飞行将面临拘留
  • RAD Studio 13 Florence:C++ 与 Delphi 现代化及 AI 集成特性解析
  • Python 构建 GitHub 热门项目 AI 分析与挖掘 Agent
  • Linux 线程控制函数详解
  • VS Code 新版禁用 Ctrl+I 快捷键唤起 Copilot AI 对话框
  • 轻小说机翻机器人:架构设计与快速部署
  • GitHub 浏览器插件实现界面中文翻译
  • 金仓数据库 Oracle 与 SQL Server 迁移适配指南
  • Midjourney 官方网址与中文支持说明
  • Gemini 2.5 Pro 技术突破与实战应用深度解析
  • HTML5+CSS3+JavaScript 实现高木同学圣诞树 GalGame 开发
  • C#初级开发者应对 AI 预测重构的策略与实战技巧
  • AI 一周大事件:万亿硬件订单、模型套壳与生态变局解读
  • Linux 下 Conda 安装与使用指南:从下载到环境管理
  • Python 转行职业规划与核心岗位技能分析
  • 前端小白必看 React Router路由配置全攻略(附避坑指南)
  • 雷达信号处理中的恒虚警率(CFAR)技术详解

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online