news 2026/6/16 16:12:40

二叉树从入门到精通:一篇搞定数据结构中的“树”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
二叉树从入门到精通:一篇搞定数据结构中的“树”

二叉树从入门到精通:一篇搞定数据结构中的“树”

读完这篇文章,你将彻底掌握二叉树的核心概念、性质、实现和应用

前言

在计算机科学中,数据结构就像程序员的“武器库”。而二叉树,无疑是这个武器库中最重要、最基础的“兵器”之一。无论是数据库索引、文件系统组织,还是算法竞赛中的各类问题,二叉树都扮演着不可或缺的角色。

今天,就让我们一起来彻底搞懂二叉树!


一、树是什么?为什么叫“树”?

1.1 树的定义

树是一种非线性的数据结构,它由 n(n≥0)个有限结点组成一个具有层次关系的集合。

之所以叫“树”,是因为它长得像一棵倒挂的树——根朝上,叶朝下

根结点 / | \ 子结点 子结点 子结点 / \ | ... ... ...

1.2 树的关键概念

概念解释例子
结点的度结点拥有的子树个数结点A有3个子树,度=3
叶结点度为0的结点没有孩子的结点
父结点有子结点的结点A是B的父结点
子结点父结点的下属结点B是A的子结点
兄弟结点相同父结点的结点B和C是兄弟
树的度树中最大的结点度最大度为6,树的度=6
树的深度/高度结点的最大层次数根为第1层,最深到第4层,高度=4
森林m棵互不相交的树的集合多棵树组成森林

1.3 树的表示方法(孩子兄弟表示法)

typedefintDataType;structNode{structNode*firstChild;// 第一个孩子结点structNode*pNextBrother;// 指向下一个兄弟结点DataType data;// 数据域};

这种表示法的精妙之处在于:任何一棵普通的树都可以用这个结构表示出来,而且不会造成空间浪费。

1.4 树的应用

树在现实生活中最常见的应用就是文件系统的目录结构

C: ├── Program Files │ ├── Python │ └── Java ├── Users │ ├── Admin │ └── Guest └── Windows └── System32

二、二叉树:每个结点最多有两个孩子

2.1 什么是二叉树?

二叉树是每个结点最多有两个子树的树结构。这两个子树分别称为左子树右子树,且左右顺序不能颠倒(有序树)。

2.2 特殊二叉树

满二叉树

每一层的结点数都达到最大值。如果层数为 K,总结点数为2^K - 1

1 / \ 2 3 / \ / \ 4 5 6 7 ← 满二叉树(三层)
完全二叉树

对于深度为 K、有 n 个结点的二叉树,每个结点都与深度为 K 的满二叉树中编号 1~n 的结点一一对应

满二叉树是完全二叉树的特例

1 / \ 2 3 / \ / 4 5 6 ← 完全二叉树(缺了7号位置)

2.3 二叉树的核心性质

性质内容
性质1第 i 层最多有2^(i-1)个结点
性质2深度为 h 的二叉树最多有2^h - 1个结点
性质3叶结点数 n0 = 度为2的结点数 n2 + 1
性质4满二叉树深度 h = log₂(n+1)

性质3的证明是面试重点!

设总结点数 N = n0 + n1 + n2

从边的角度:N - 1 = n1 + 2×n2(每个结点向上有1条边,根除外)

两式相减可得:n0 = n2 + 1

2.4 完全二叉树的编号规律

对于按从上到下、从左到右编号的完全二叉树(根结点编号为0):

0 / \ 1 2 / \ / \ 3 4 5 6
需求公式
双亲结点(i - 1) / 2
左孩子2i + 1(需 < n)
右孩子2i + 2(需 < n)

三、二叉树的存储结构

3.1 顺序存储(数组)

使用数组存储二叉树,只适合完全二叉树,否则会造成大量空间浪费。

完全二叉树用数组存: 非完全二叉树用数组存: [1, 2, 3, 4, 5, 6, 7] [1, 2, 3, 空, 空, 4, 5, 空, ...] 紧凑无浪费 大量空洞,浪费空间

💡实际应用:堆(Heap)就是用数组存储的完全二叉树。

3.2 链式存储(最常用)

每个结点包含数据域和左右指针:

typedefintBTDataType;typedefstructBinaryTreeNode{BTDataType data;// 数据域structBinaryTreeNode*left;// 左孩子指针structBinaryTreeNode*right;// 右孩子指针}BTNode;

如果需要频繁访问父结点,可以使用三叉链表(多一个 parent 指针)。


四、堆:一种特殊的完全二叉树

4.1 什么是堆?

堆是一种用数组存储的完全二叉树,分为两种:

类型特点
大根堆任意结点 ≥ 其子结点(根最大)
小根堆任意结点 ≤ 其子结点(根最小)

4.2 堆的核心操作

向下调整(AdjustDown)

前提:左右子树已经是堆。

voidAdjustDown(int*a,intn,introot){intparent=root;intchild=parent*2+1;// 左孩子while(child<n){// 选出左右孩子中较大的(大根堆)if(child+1<n&&a[child+1]>a[child]){child++;}if(a[child]>a[parent]){Swap(&a[child],&a[parent]);parent=child;child=parent*2+1;}else{break;}}}
向上调整(AdjustUp)

用于插入元素后的调整:

voidAdjustUp(int*a,intchild){intparent=(child-1)/2;while(child>0){if(a[child]>a[parent]){Swap(&a[child],&a[parent]);child=parent;parent=(child-1)/2;}else{break;}}}

4.3 建堆的时间复杂度

建堆的时间复杂度是O(N),这是一个经典结论。

很多人以为建堆是 O(N log N),实际上从最后一个非叶子结点开始向下调整,总步数约为 2^h - 1 - h ≈ N。

4.4 堆的代码实现

typedefstructHeap{HPDataType*a;intsize;intcapacity;}Heap;// 堆的插入voidHeapPush(Heap*hp,HPDataType x){if(hp->size==hp->capacity){// 扩容intnewCapacity=hp->capacity==0?4:hp->capacity*2;HPDataType*tmp=realloc(hp->a,sizeof(HPDataType)*newCapacity);hp->a=tmp;hp->capacity=newCapacity;}hp->a[hp->size]=x;hp->size++;AdjustUp(hp->a,hp->size-1);}// 堆的删除(删除堆顶)voidHeapPop(Heap*hp){assert(!HeapEmpty(hp));Swap(&hp->a[0],&hp->a[hp->size-1]);hp->size--;AdjustDown(hp->a,hp->size,0);}

五、堆的两大应用

5.1 堆排序

堆排序分为两步:

  1. 建堆:升序建大堆,降序建小堆
  2. 排序:每次把堆顶元素换到最后,再向下调整
voidHeapSort(int*a,intn){// 建堆 O(N)for(inti=(n-1-1)/2;i>=0;i--){AdjustDown(a,n,i);}// 排序 O(N log N)intend=n-1;while(end>0){Swap(&a[0],&a[end]);AdjustDown(a,end,0);end--;}}

时间复杂度:O(N log N)
空间复杂度:O(1)

5.2 Top-K 问题

问题:从 N 个数据中找出最大(或最小)的前 K 个,N 极大(如 100 亿)。

解法

  1. 用前 K 个元素建堆(找最大建小堆,找最小建大堆
  2. 遍历剩余 N-K 个元素,比堆顶大则替换并向下调整
  3. 最后堆中的 K 个元素就是答案
voidPrintTopK(int*a,intn,intk){// 1. 用前k个元素建小堆for(inti=(k-2)/2;i>=0;i--){AdjustDown(a,k,i);}// 2. 遍历剩余元素for(inti=k;i<n;i++){if(a[i]>a[0]){a[0]=a[i];AdjustDown(a,k,0);}}// 3. 打印结果for(inti=0;i<k;i++){printf("%d ",a[i]);}}

时间复杂度:O(N log K)
空间复杂度:O(K)


六、二叉树的遍历

6.1 四种遍历方式

遍历方式顺序结果(图示二叉树)
前序遍历根 → 左 → 右1 2 3 4 5 6
中序遍历左 → 根 → 右3 2 1 5 4 6
后序遍历左 → 右 → 根3 2 5 6 4 1
层序遍历逐层从左到右1 2 4 3 5 6

6.2 递归实现(简洁优雅)

// 前序遍历voidPreOrder(BTNode*root){if(root==NULL)return;printf("%d ",root->data);PreOrder(root->left);PreOrder(root->right);}// 中序遍历voidInOrder(BTNode*root){if(root==NULL)return;InOrder(root->left);printf("%d ",root->data);InOrder(root->right);}// 后序遍历voidPostOrder(BTNode*root){if(root==NULL)return;PostOrder(root->left);PostOrder(root->right);printf("%d ",root->data);}

6.3 层序遍历(使用队列)

voidLevelOrder(BTNode*root){if(root==NULL)return;Queue q;QueueInit(&q);QueuePush(&q,root);while(!QueueEmpty(&q)){BTNode*front=QueueFront(&q);QueuePop(&q);printf("%d ",front->data);if(front->left)QueuePush(&q,front->left);if(front->right)QueuePush(&q,front->right);}QueueDestroy(&q);}

七、常见二叉树面试题

7.1 求二叉树结点个数

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

7.2 求叶子结点个数

intBinaryTreeLeafSize(BTNode*root){if(root==NULL)return0;if(root->left==NULL&&root->right==NULL)return1;returnBinaryTreeLeafSize(root->left)+BinaryTreeLeafSize(root->right);}

7.3 求第k层结点个数

intBinaryTreeLevelKSize(BTNode*root,intk){if(root==NULL)return0;if(k==1)return1;returnBinaryTreeLevelKSize(root->left,k-1)+BinaryTreeLevelKSize(root->right,k-1);}

7.4 二叉树的高度

intBinaryTreeHeight(BTNode*root){if(root==NULL)return0;intleftHeight=BinaryTreeHeight(root->left);intrightHeight=BinaryTreeHeight(root->right);return(leftHeight>rightHeight?leftHeight:rightHeight)+1;}

7.5 根据遍历序列重建二叉树

关键:前序(或后序)确定根,中序确定左右子树。

例题:已知前序遍历EFHIGJK,中序遍历HFIEJKG,求根结点?

  • 前序第一个结点E就是根结点 ✅

八、经典OJ题推荐

题目难度考察点
单值二叉树简单递归遍历
相同的树简单同步遍历
对称二叉树简单镜像比较
二叉树的前序遍历简单非递归遍历
另一棵树的子树简单递归匹配
二叉树的最大深度简单分治思想

九、精选练习题及答案解析

题目1

某二叉树共有399个结点,其中有199个度为2的结点,则叶子结点数为?

答案:B. 200

解析:根据性质n0 = n2 + 1 = 199 + 1 = 200

题目2

下列数据结构中,不适合采用顺序存储结构的是?

A. 非完全二叉树 B. 堆 C. 队列 D. 栈

答案:A

解析:非完全二叉树用顺序存储会产生大量空间浪费。

题目3

在具有2n个结点的完全二叉树中,叶子结点个数为?

答案:A. n

解析:完全二叉树中,叶子结点数 = ⌈n/2⌉,这里总结点数 2n 为偶数,叶子结点 = n。

题目4

一棵完全二叉树的结点数为531个,这棵树的高度为?

答案:B. 10

解析:2^9 - 1 = 511 < 531 ≤ 2^10 - 1 = 1023,所以高度为10。

题目5

一个具有767个结点的完全二叉树,叶子结点个数为?

答案:B. 384

解析:叶子结点 = ⌈767/2⌉ = 384


十、总结

知识点核心要点
树的概念递归定义,根朝上叶朝下
二叉树性质n0 = n2 + 1,第i层最多2^(i-1)个结点
完全二叉树适合顺序存储,父子结点有公式关系
大根堆/小根堆,向下调整O(log N)
堆排序O(N log N),空间O(1),不稳定
Top-K建小堆找最大K个,O(N log K)
遍历前中后序(递归),层序(队列)

二叉树是学习更复杂数据结构(如二叉搜索树、AVL树、红黑树、B树)的基石。掌握好二叉树,你就拿到了通往高级数据结构的钥匙


📌本文代码均为纯C实现,易于理解,可直接运行测试。

希望这篇文章对你有所帮助!如有疑问,欢迎在评论区留言讨论~

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/8 3:19:16

Krita Vision Tools:AI智能选区工具的终极指南

Krita Vision Tools&#xff1a;AI智能选区工具的终极指南 【免费下载链接】krita-vision-tools Krita plugin which adds selection tools to mask objects with a single click, or by drawing a bounding box. 项目地址: https://gitcode.com/gh_mirrors/kr/krita-vision-…

作者头像 李华
网站建设 2026/6/8 0:54:15

城通网盘高速下载解析器:告别限速的浏览器端解决方案

城通网盘高速下载解析器&#xff1a;告别限速的浏览器端解决方案 【免费下载链接】ctfileGet 获取城通网盘一次性直连地址 项目地址: https://gitcode.com/gh_mirrors/ct/ctfileGet 城通网盘作为国内常用的文件分享平台&#xff0c;其下载限速问题长期困扰着用户。ctfil…

作者头像 李华
网站建设 2026/6/8 8:25:55

遗传算法工程实战:动态算子设计与工业级调参指南

1. 这不是教科书里的遗传算法&#xff0c;而是我调试了73次后才敢写的实操指南“遗传算法”这四个字&#xff0c;听上去像生物课上讲DNA双螺旋时顺带提的一句术语&#xff0c;又像AI面试题里那个永远答不全的“请手推GA流程”。但真实情况是&#xff1a;我在工业缺陷检测项目里…

作者头像 李华