跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C算法

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

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

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

一、二叉树的链式结构

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

// 链式二叉树的节点
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('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;
}

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

文章配图

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

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

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. 四、综合代码
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • 本地文件上传至服务器的常用方法与工具
  • 基于 Python 的 CCS 自动项目生成与编译工具实现
  • 2026 年 AI 大语言模型评测:GPT-5.2、Claude 4.5 与国产模型对比
  • cJSON 1.7.19 源码深度分析:数据结构、解析流程与注释实践
  • TI AFE5816:16 通道超声波模拟前端 (AFE) 详解
  • Flutter 底部导航与 TabBar 多页切换及鸿蒙适配
  • ERNIE-4.5-0.3B 轻量模型本地部署与效能实测
  • OpenClaw 多端交互实测指南:Web/TUI/钉钉集成配置
  • Rust 桌面 GUI 框架 2025 年横评与选型指南
  • 耳机阻抗与前端适配:32Ω、150Ω、300Ω耳机的功放推力分析
  • OpenSpec 实战:用规范驱动开发解决 AI 编程协作难题
  • Meta 提出多 token 模型训练新方法,有望提升生成速度与智能性
  • 大语言模型 LLM 微调策略详解
  • ERNIE-4.5-0.3B 轻量模型部署指南与性能测评
  • OpenClaw 对接飞书机器人:消息无响应与 Gateway 断开排查
  • 企业级图像 AIGC 技术观察:Seedream 4.0 模型能力与应用场景
  • 基于 Qwen3-VL 的操作视频智能评分系统部署实战
  • Vivado 许可证获取与配置指南
  • GitHub 下载速度提升方案汇总
  • Mac Mini 部署 OpenClaw 实战指南:构建本地 AI 智能体

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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