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

数据结构:二叉树基础概念与链式存储实现

本文详细讲解了二叉树的数据结构定义、基本术语及性质,重点阐述了链式存储的实现方式。内容包括二叉树的创建、前中后序遍历、层序遍历算法,以及节点计数、高度计算、查找节点和判断完全二叉树等核心功能的代码实现。通过完整的 C 语言代码示例,展示了如何利用队列辅助层序遍历及完全二叉树判定,适合希望深入理解树形结构及其操作的开发者参考。

MongoKing发布于 2026/3/21更新于 2026/10/370 浏览
数据结构:二叉树基础概念与链式存储实现

数据结构:二叉树基础概念与链式存储实现

一、树的基础概念

1. 树的定义

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

2. 常见术语

  • 结点的度:一个结点含有的子树的个数。
  • 叶结点:度为 0 的结点。
  • 双亲结点:若一个结点含有子结点,则这个结点称为其子结点的父结点。
  • 树的高度:树中结点的最大层次。

树的结构示意图

二、二叉树详解

1. 什么是二叉树

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

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

二叉树组成

2. 特殊的二叉树

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

注意:满二叉树是一种特殊的完全二叉树。完全二叉树的编号是连续的,中间断开则不是。

满二叉树与完全二叉树

3. 顺序存储与性质

对于完全二叉树,我们可以用数组进行存储,下标对应编号。逻辑上是树,物理上是数组。

重要性质:

  1. 第 i 层最多有 2^(i-1) 个结点。
  2. 深度为 h 的二叉树最大结点数为 2^h - 1。
  3. 叶子结点数 n0 = 度为 2 的结点数 n2 + 1。
  4. 若父亲在数组中下标为 i,则左孩子为 2i+1,右孩子为 2i+2;孩子下标为 i,父亲为 (i-1)/2。

三、二叉树的链式存储实现

非完全二叉树不适合用数组存储,否则空间浪费严重。我们通常采用链式存储,每个结点包含数据域和左右指针域。

1. 节点定义

我们需要定义一个二叉树结构体,包含数据类型、左孩子指针和右孩子指针。为了方便后续修改类型,使用 typedef 重定义数据类型。

// BTNode.h
typedef char BTDataType;

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

2. 构建二叉树

通过前序遍历的数组来构建二叉树是一个经典方法。遇到 '#' 表示空结点。

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

3. 遍历操作

遍历是二叉树的核心操作,主要有前序、中序、后序和层序四种。

前序遍历:根 -> 左 -> 右

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* node = QueueFront(&Q);
        printf("%c ", node->data);
        QueuePop(&Q);
        if (node->left) {
            QueuePush(&Q, node->left);
        }
        if (node->right) {
            QueuePush(&Q, node->right);
        }
    }
    QueueDestroy(&Q);
}

4. 其他常用功能

除了遍历,我们还需要计算节点个数、高度、查找特定值以及判断是否为完全二叉树。

节点个数:递归计算左右子树大小加 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;
    }
    return fmax(BinaryTreeHight(root->left), BinaryTreeHight(root->right)) + 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* node = QueueFront(&Q);
        QueuePop(&Q);
        if (node == NULL) {
            break;
        }
        QueuePush(&Q, node->left);
        QueuePush(&Q, node->right);
    }
    while (!QueueEmpty(&Q)) {
        BTNode* node = QueueFront(&Q);
        QueuePop(&Q);
        if (node != NULL) {
            QueueDestroy(&Q);
            return 0;
        }
    }
    QueueDestroy(&Q);
    return 1;
}

四、完整代码示例

为了便于理解,这里整理了完整的头文件与源文件结构。请确保所有文件在同一目录下编译运行。

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 failed");
        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;
}

BTNode.h

#pragma once
#include "Queue.h"

typedef char BTDataType;

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

BTNode* BinaryTreeCreate(BTDataType* ch, int* pi);
void BinaryTreePrevOrder(BTNode* root);
void BinaryTreeInOrder(BTNode* root);
void BinaryTreePostOrder(BTNode* root);
int BinaryTreeSize(BTNode* root);
int BinaryTreeLeafSize(BTNode* root);
int BinaryTreeHight(BTNode* root);
void BinaryTreeDestory(BTNode** root);
int BinaryTreeLevelKSize(BTNode* root, int k);
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
int BinaryTreeComplete(BTNode* root);
void BinaryTreeLevelOrder(BTNode* root);

BTNode.c

#include "BTNode.h"

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

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* node = QueueFront(&Q);
        printf("%c ", node->data);
        QueuePop(&Q);
        if (node->left) {
            QueuePush(&Q, node->left);
        }
        if (node->right) {
            QueuePush(&Q, node->right);
        }
    }
    QueueDestroy(&Q);
}

void BinaryTreeDestory(BTNode** root) {
    if (*root == NULL) {
        return;
    }
    BinaryTreeDestory(&(*root)->left);
    BinaryTreeDestory(&(*root)->right);
    free(*root);
    *root = NULL;
}

int BinaryTreeSize(BTNode* root) {
    return root == NULL ? 0 : BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}

int BinaryTreeLeafSize(BTNode* root) {
    if (root == NULL) {
        return 0;
    }
    if (root->left == NULL && root->right == NULL) {
        return 1;
    }
    return BinaryTreeLeafSize(root->left) + BinaryTreeLeafSize(root->right);
}

int BinaryTreeHight(BTNode* root) {
    if (root == NULL) {
        return 0;
    }
    return fmax(BinaryTreeHight(root->left), BinaryTreeHight(root->right)) + 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);
}

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* node = QueueFront(&Q);
        QueuePop(&Q);
        if (node == NULL) {
            break;
        }
        QueuePush(&Q, node->left);
        QueuePush(&Q, node->right);
    }
    while (!QueueEmpty(&Q)) {
        BTNode* node = QueueFront(&Q);
        QueuePop(&Q);
        if (node != NULL) {
            QueueDestroy(&Q);
            return 0;
        }
    }
    QueueDestroy(&Q);
    return 1;
}

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);
    } else {
        printf("未找到\n");
    }

    if (BinaryTreeComplete(T)) {
        printf("是完全二叉树\n");
    } else {
        printf("不是完全二叉树\n");
    }

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

    BinaryTreeDestory(&T);
    return 0;
}

以上就是二叉树的基础理论与 C 语言实现的完整讲解。掌握这些内容后,建议多动手编写代码,加深理解。

目录

  1. 数据结构:二叉树基础概念与链式存储实现
  2. 一、树的基础概念
  3. 1. 树的定义
  4. 2. 常见术语
  5. 二、二叉树详解
  6. 1. 什么是二叉树
  7. 2. 特殊的二叉树
  8. 3. 顺序存储与性质
  9. 三、二叉树的链式存储实现
  10. 1. 节点定义
  11. 2. 构建二叉树
  12. 3. 遍历操作
  13. 4. 其他常用功能
  14. 四、完整代码示例
  15. Queue.h
  16. Queue.c
  17. BTNode.h
  18. BTNode.c
  19. Test.c

更多推荐文章

查看全部
  • 面向城市空中交通的多无人机协同路径规划研究
  • 基于腾讯混元 3D 大模型实现静态图片转 3D 模型教程
  • CSS 背景样式详解:颜色、图片与复合属性
  • 多模态处理技术与大模型架构下的AI解决方案落地实践
  • 国产 AI 智能体平台对比:腾讯、字节、阿里、百度等主流方案汇总
  • Spring AI 入门与实战:构建生成式 AI 应用
  • Ubuntu 24.04 安装 NVIDIA 显卡驱动及 CUDA 开发环境指南
  • Windows 下使用 Docker Desktop 和 Ubuntu 部署 Qwen 大模型
  • VS Code + GitHub Copilot 实战指南:从配置到高效协作
  • TongWeb 嵌入式版本安装指南
  • Flutter 组件 tavily_dart 鸿蒙适配与 AI 搜索集成实战
  • 基于回调接口实现 AI 助手接入企业微信群聊
  • NVIDIA AI Playground 免费调用 GLM-4.7 与 MiniMax M2.1 模型指南
  • OpenClaw + Ollama 全离线部署指南 (Windows)
  • 使用 Portainer 管理 Docker 容器并实现公网远程访问
  • GitHub Copilot 使用体验与功能评测
  • Spring Cloud Gateway 网关实战:路由配置、过滤器与限流鉴权
  • Java 算法实践:动态规划
  • LLaMA2 模型架构深度解析
  • 学术论文知网 AIGC 检测原理与降重实操指南

相关免费在线工具

  • 加密/解密文本

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