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

数据结构:二叉树初阶与链式实现

二叉树作为数据结构中的核心非线性结构,涵盖定义、性质及特殊形态。本文重点阐述链式存储实现,包括前中后序递归遍历、层序遍历队列实现,以及节点计数、高度计算、查找与完全性判断等关键算法。通过完整的 C 语言代码示例,展示从节点定义到功能测试的工程化落地过程,适合初学者构建扎实的数据结构基础。

微码行者发布于 2026/2/23更新于 2026/10/2496 浏览
数据结构:二叉树初阶与链式实现

数据结构:二叉树初阶与链式实现

一、树是什么?

1. 树的定义

树是一种非线性数据结构,由 n(n>=0)个有限结点组成一个具有层次关系的集合。有一个特殊的结点称为根结点,根结点没有前驱结点。除根结点外,其余结点被分成 M(M>0) 个互不相交的集合 T1、T2、……、Tm,其中每一个集合 Ti(1<= i <= m) 又是一棵结构与树类似的子树。每棵子树的根结点有且只有一个前驱,可以有 0 个或多个后继。

2. 常见术语

树结构示例

  • 结点的度:一个结点含有的子树的个数称为该结点的度。(例如 A 的度为 6)
  • 叶结点或终端结点:度为 0 的结点称为叶结点。(例如 B、H、P 等)
  • 双亲结点或父结点:若一个结点含有子结点,则这个结点称为其子结点的父结点。(例如 A 为 B 的父结点)
  • 子结点:一个结点含有的子树的根结点称为该结点的子结点。(例如 B 为 A 的子结点)
  • 树的高度或深度:树中结点的最大层次。(例如该树高度为 4)

二、二叉树

1. 二叉树的概念

二叉树是一种特殊的树,其树的度最大为 2。也就是说,一个结点最多有两个分叉。由于普通树的结构复杂,实际应用中最常用的是二叉树。

2. 二叉树的组成

二叉树由一个根结点加上两棵别称为左子树和右子树的二叉树组成。注意二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树。

二叉树组成

3. 特殊的二叉树

  • 满二叉树:一个二叉树,如果每一层的结点数都达到最大值,则这个二叉树就是满二叉树。如果一个二叉树的层数为 K,且结点总数是 2^K - 1,则它就是满二叉树。
  • 完全二叉树:对于深度为 K 的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 K 的满二叉树中编号从 1 至 n 的结点一一对应时称之为完全二叉树。要注意满二叉树是一种特殊的完全二叉树。

满二叉树与完全二叉树

完全二叉树的编号是连续的,中间断开则不是完全二叉树。

4. 二叉树的顺序存储

对于完全二叉树,由于其编号连续,可以用数组进行存储,用下标来进行编号。二叉树顺序存储在物理上是一个数组,而在逻辑上是一颗二叉树。

5. 二叉树的性质

  1. 若规定根结点的层数为 1,则一棵非空二叉树的第 i 层上最多有 2^(i-1) 个结点。
  2. 若规定根结点的层数为 1,则深度为 h 的二叉树的最大结点数是 2^h - 1。
  3. 对任何一棵二叉树,如果度为 0 其叶结点个数为 n₀,度为 2 的分支结点个数为 n₂,则有 n₀ = n₂ + 1。
  4. 若规定根结点的层数为 1,具有 n 个结点的满二叉树的深度,h = log₂(n + 1)。

重要性质: 对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有结点从 0 开始编号:

  • 若父亲在数组中下标为 i,则该父亲的左孩子下标为 2 * i + 1;右孩子下标为 2 * i + 2。
  • 若孩子在数组中下标为 i,则该孩子的父亲下标为 (i - 1) / 2。 注意:前提是这些下标所对应的值存在。

三、二叉树的实现

1. 链式存储

前面提到完全二叉树可以用数组存储,而非完全二叉树若强行用数组会浪费大量空间。此时我们使用链式存储。

链式存储即用链表来表示一颗二叉树,用链来指示元素的逻辑关系。通常的方法创建一链表,使每个结点由三个域组成:数据域和左右指针域。左右指针分别用来给出该结点左孩子和右孩子所在结点的地址。

2. 节点定义

与单链表类似,二叉树中的每一个节点有以下三个成员:

  1. 节点中存储的数据
  2. 指向节点左孩子的指针
  3. 指向节点右孩子的指针

为了便于类型修改,我们使用 typedef 重定义数据类型。

// BTNode.h
#pragma once
#include "Queue.h"

typedef char BTDataType;

typedef struct BinaryTreeNode {
    BTDataType data;
    struct BinaryTreeNode* left;
    struct BinaryTreeNode* right;
}BTNode;

3. 构建二叉树

这里通过一个前序遍历的数组 "ABD##E#H##CF##G##" 来构建二叉树。'#' 代表空节点。

// BTNode.c
BTNode* BinaryTreeCreate(BTDataType* ch, int* pi) {
    if (ch[*pi] == '#') {
        return NULL;
    }
    BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
    if (newnode == NULL) {
        perror("malloc fail");
        return NULL;
    }
    newnode->data = ch[(*pi)++];
    newnode->left = BinaryTreeCreate(ch, pi);
    (*pi)++;
    newnode->right = BinaryTreeCreate(ch, pi);
    return newnode;
}

4. 二叉树遍历

前序、中序、后序遍历

这三种遍历主要在于访问根节点的时机不同,均采用递归实现。

  • 前序遍历:根 -> 左 -> 右
  • 中序遍历:左 -> 根 -> 右
  • 后序遍历:左 -> 右 -> 根
// 前序遍历
void BinaryTreePrevOrder(BTNode* root) {
    if (root == NULL) {
        return;
    }
    printf("%c ", root->data);
    BinaryTreePrevOrder(root->left);
    BinaryTreePrevOrder(root->right);
}

// 中序遍历
void BinaryTreeInOrder(BTNode* root) {
    if (root == NULL) {
        return;
    }
    BinaryTreeInOrder(root->left);
    printf("%c ", root->data);
    BinaryTreeInOrder(root->right);
}

// 后序遍历
void BinaryTreePostOrder(BTNode* root) {
    if (root == NULL) {
        return;
    }
    BinaryTreePostOrder(root->left);
    BinaryTreePostOrder(root->right);
    printf("%c ", root->data);
}
层序遍历

层序遍历需要借助队列来实现。思路是每次取出队头结点,将其左右孩子入队,直到队列为空。

// 层序遍历
void BinaryTreeLevelOrder(BTNode* root) {
    Queue Q;
    QueueInit(&Q);
    if (root) {
        QueuePush(&Q, root);
    }
    while (!QueueEmpty(&Q)) {
        BTNode* front = QueueFront(&Q);
        printf("%c ", front->data);
        QueuePop(&Q);
        if (front->left) {
            QueuePush(&Q, front->left);
        }
        if (front->right) {
            QueuePush(&Q, front->right);
        }
    }
    QueueDestroy(&Q);
}

5. 其他常用操作

节点个数计算

利用递归,根节点为空返回 0,否则返回左子树 + 右子树 + 1。

int BinaryTreeSize(BTNode* root) {
    return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
二叉树高度计算

返回左右子树高度较高的一边并 + 1。

int BinaryTreeHight(BTNode* root) {
    if (root == NULL) {
        return 0;
    }
    int leftH = BinaryTreeHight(root->left);
    int rightH = BinaryTreeHight(root->right);
    return (leftH > rightH ? leftH : rightH) + 1;
}
第 k 层节点个数

初始 k,若该层不是目标层次,就 k - 1 并递归左右子树。到达第 k 层返回 1。

int BinaryTreeLevelKSize(BTNode* root, int k) {
    if (root == NULL) {
        return 0;
    }
    if (k == 1) {
        return 1;
    }
    return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
查找值为 x 的节点

递归查找,先判断根节点,再找左子树,若未找到再找右子树。

BTNode* BinaryTreeFind(BTNode* root, BTDataType x) {
    if (root == NULL) {
        return NULL;
    }
    if (root->data == x) {
        return root;
    }
    BTNode* ret1 = BinaryTreeFind(root->left, x);
    if (ret1) {
        return ret1;
    }
    BTNode* ret2 = BinaryTreeFind(root->right, x);
    if (ret2) {
        return ret2;
    }
    return NULL;
}
判断是否是完全二叉树

利用层序遍历,遇到空节点后,后续不应再有非空节点。

int BinaryTreeComplete(BTNode* root) {
    Queue Q;
    QueueInit(&Q);
    if (root) {
        QueuePush(&Q, root);
    }
    while (!QueueEmpty(&Q)) {
        BTNode* front = QueueFront(&Q);
        QueuePop(&Q);
        if (front == NULL) {
            break;
        }
        QueuePush(&Q, front->left);
        QueuePush(&Q, front->right);
    }
    while (!QueueEmpty(&Q)) {
        BTNode* front = QueueFront(&Q);
        QueuePop(&Q);
        if (front != NULL) {
            QueueDestroy(&Q);
            return 0;
        }
    }
    QueueDestroy(&Q);
    return 1;
}

四、完整代码结构

1. 队列实现 (Queue.h / Queue.c)

// Queue.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>

typedef int QDataType;

typedef struct QListNode {
    QDataType data;
    struct QListNode* next;
}QNode;

typedef struct Queue {
    QNode* front;
    QNode* tail;
    int size;
}Queue;

void QueueInit(Queue* q);
void QueuePush(Queue* q, QDataType data);
void QueuePop(Queue* q);
QDataType QueueFront(Queue* q);
QDataType QueueBack(Queue* q);
int QueueSize(Queue* q);
int QueueEmpty(Queue* q);
void QueueDestroy(Queue* q);
// Queue.c
#include "Queue.h"

void QueueInit(Queue* q) {
    assert(q);
    q->front = NULL;
    q->tail = NULL;
    q->size = 0;
}

void QueuePush(Queue* q, QDataType data) {
    assert(q);
    QNode* tmp = (QNode*)malloc(sizeof(QNode));
    if (tmp == NULL) {
        perror("malloc");
        return;
    }
    tmp->data = data;
    tmp->next = NULL;
    if (q->size == 0) {
        q->front = tmp;
        q->tail = tmp;
    } else {
        q->tail->next = tmp;
        q->tail = tmp;
    }
    q->size++;
}

void QueuePop(Queue* q) {
    assert(q);
    assert(q->size > 0);
    if (q->size == 1) {
        free(q->front);
        q->front = NULL;
        q->tail = NULL;
    } else {
        QNode* del = q->front;
        q->front = q->front->next;
        free(del);
    }
    q->size--;
}

QDataType QueueFront(Queue* q) {
    assert(q);
    assert(q->size > 0);
    return q->front->data;
}

QDataType QueueBack(Queue* q) {
    assert(q);
    assert(q->size > 0);
    return q->tail->data;
}

int QueueSize(Queue* q) {
    assert(q);
    return q->size;
}

int QueueEmpty(Queue* q) {
    assert(q);
    return q->size == 0;
}

void QueueDestroy(Queue* q) {
    assert(q);
    QNode* cur = q->front;
    while (cur) {
        QNode* next = cur->next;
        free(cur);
        cur = next;
    }
    q->front = q->tail = NULL;
    q->size = 0;
}

2. 测试文件 (Test.c)

#include "BTNode.h"

int main() {
    char ch[100] = "ABD##E#H##CF##G##";
    int i = 0;
    BTNode* T = BinaryTreeCreate(ch, &i);

    printf("节点个数:%d\n", BinaryTreeSize(T));
    printf("叶子节点个数:%d\n", BinaryTreeLeafSize(T));
    printf("高度:%d\n", BinaryTreeHight(T));
    printf("第 3 层节点个数:%d\n", BinaryTreeLevelKSize(T, 3));

    BTNode* ret = BinaryTreeFind(T, 'G');
    if (ret) {
        printf("找到 G: %c\n", ret->data);
    }

    printf("是否完全二叉树:%s\n", BinaryTreeComplete(T) ? "是" : "否");

    printf("前序:");
    BinaryTreePrevOrder(T);
    printf("\n");
    printf("中序:");
    BinaryTreeInOrder(T);
    printf("\n");
    printf("后序:");
    BinaryTreePostOrder(T);
    printf("\n");
    printf("层序:");
    BinaryTreeLevelOrder(T);
    printf("\n");

    BinaryTreeDestory(&T);
    return 0;
}

本文涵盖了二叉树的核心概念与工程实现细节。通过上述代码,你可以直接编译运行并观察不同遍历方式及统计函数的效果。在实际开发中,理解这些底层逻辑有助于更好地设计算法和处理复杂的数据关系。

目录

  1. 数据结构:二叉树初阶与链式实现
  2. 一、树是什么?
  3. 1. 树的定义
  4. 2. 常见术语
  5. 二、二叉树
  6. 1. 二叉树的概念
  7. 2. 二叉树的组成
  8. 3. 特殊的二叉树
  9. 4. 二叉树的顺序存储
  10. 5. 二叉树的性质
  11. 三、二叉树的实现
  12. 1. 链式存储
  13. 2. 节点定义
  14. 3. 构建二叉树
  15. 4. 二叉树遍历
  16. 前序、中序、后序遍历
  17. 层序遍历
  18. 5. 其他常用操作
  19. 节点个数计算
  20. 二叉树高度计算
  21. 第 k 层节点个数
  22. 查找值为 x 的节点
  23. 判断是否是完全二叉树
  24. 四、完整代码结构
  25. 1. 队列实现 (Queue.h / Queue.c)
  26. 2. 测试文件 (Test.c)

更多推荐文章

查看全部
  • Android 性能优化项目实战:一线互联网大厂开发案例合集
  • 企业级工作流引擎低代码开发实战:RuoYi-Flowable-Plus 指南
  • AIGC 试用:AI 辅助软件开发流程实践
  • PyTorch-2.x-Universal 镜像搭建 AI 绘画开发环境指南
  • 程序员求职现状分析:行业寒冬下的赛道选择与技能提升
  • MySQL 表基础语法:增删查与函数核心技巧
  • 高鋒集團與 Web3Labs:以資本與生態賦能傳統企業 Web3 轉型
  • AI 产品经理面试指南与职业发展路径分析
  • C++ 关联容器实战:Set、Map 与键值对核心解析
  • Python 3.11.14 安装指南及安全修复说明
  • 程序员进阶技术专家的核心能力与源码深度解析
  • GitHub 学生开发者包认证指南
  • 常见 Web 漏洞渗透及防护方法
  • 智源与港中文发布首个多功能3D医学多模态大模型M3D系列
  • AI 大模型技术入门、进阶与职业发展路径详解
  • VS Code 远程连接服务器后 GitHub Copilot 无法使用
  • 基于大模型的学术论文生成工具功能介绍
  • 使用 Python 和 AI 构建智能害虫识别助手
  • 深入理解数据结构中的时间与空间复杂度
  • 机器学习核心算法实战笔记:从 KNN 到集成学习

相关免费在线工具

  • 加密/解密文本

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