少年恃险若平地
独倚长剑凌清秋
🔥个人专栏
期待小伙伴们的支持与关注!!!
目录

我们数据结构的前几章都是线性结构,而我们今天来学习非线性结构的数形结构--树
那什么是树形结构呢?
如图所示:根在下,叶朝上 的就是我们生活中的树
树的定义与判定
树是一种 非线性 的数据结构,它是由 n ( n>=0 )个有限结点组成一个具有层次关系的集合把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是 根朝上,而叶朝下 的树的定义
<1>有且仅有一个特殊的结点,称为根结点,根节点没有前驱结点 <2>除根节点外,其余结点被分成M(M>0)个互不相交的集合:T1、T2、… …、Tm
其中每一个集合Ti(1<= i <= m)又是一棵结构与树类似的子树
每棵子树的根结点有且只有一个前驱,可以有零个或多个后继
<3>树是递归定义的 树的判定
<1>树形结构中,子树之间不能有交集,否则就不是树形结构 <2>除了根节点以外,每个节点有且只有一个父节点 <3>一颗N节点的数有N-1条边 像以下的结构就是树形结构
像以下 子树之间有交集 的结构就 不能 叫做树形结构
树的相关概念

|
节点的度
:
一个节点含有的子树的个数称为该节点的度
; 如上图:
A
的节点度为
6
|
|
叶节点或终端节点
:
度为0的节点称为叶节点
; 如上图:
B
、
C
、
H
、
I...
等节点为叶节点
|
|
非终端节点或分支节点
:
度不为0的节点
; 如上图:
D
、
E
、
F
、
G...
等节点为分支节点
|
|
双亲节点或父节点
:
若一个节点含有子节点,则这个节点称为其子节点的父节点
; 如上图:
A
是
B
的父节点
|
|
孩子节点或子节点:
一个节点含有的子树的根节点称为该节点的子节点
; 如上图:
B
是
A
的孩子节点
|
|
兄弟节点
:
具有相同父节点的节点互称为兄弟节点
; 如上图:
B
、
C
是兄弟节点
|
|
树的度
:
一棵树中,最大的节点的度称为树的度
; 如上图:树的度为
6
|
|
节点的层次
:
从根开始定义起,根为第1层,根的子节点为第2层,以此类推
|
|
树的高度或深度
:
树中节点的最大层次
; 如上图:树的高度为
4
|
|
堂兄弟节点
:
双亲在同一层的节点互为堂兄弟
;如上图:
H
、
I
互为兄弟节点
|
|
节点的祖先
:
从根到该节点所经分支上的所有节点
;如上图:
A
是所有节点的祖先
|
|
子孙
:
以某节点为根的子树中任一节点都称为该节点的子孙
;如上图:所有节点都是
A
的子孙
|
|
森林
:
由m(m>0)棵互不相交的树的集合称为森林
|
结点的度
结点拥有的子树数目称为结点的 度
结点层次
从 根开始定义 起,根为第一层,根的孩子为第二层,以此类推
树的深度
树中结点的 最大层次 数称为树的深度或高度
树的运用
以下是文件系统中目录的树的运用

树的表示
树结构相对线性表就比较复杂了,要存储表示起来就比较麻烦了, 既然保存值域,也要保存结点和结点之间 的关系 ,实际中树有很多种表示方式如:双亲表示法,孩子表示法、孩子双亲表示法以及孩子兄弟表示法等。我们这里就简单的了解其中最常用的 孩子兄弟表示法typedef char BTDataType; typedef struct BinaryTreeNode { struct BinaryTreeNode* left; // 第一个孩子结点 struct BinaryTreeNode* right; // 指向其下一个兄弟结点 BTDataType data; // 结点中的数据域 }BTNode;
二叉树的概念及结构
二叉树的概念
二叉树(Binary Tree):是一个n(n>=0)个节点所构成的集合
该集合分为空树(n = 0),或者非空树
对于非空树:
<1>有且仅有一个 根节点 <2>由一个 根节点 加上两棵 左子树 和 右子树 (别称)的二叉树组成二叉树与树一样具有 递归 性质,二叉树的特性主要有以下两点:<1>二叉树 不存在度大于2的结点<2>二叉树的子树 有左右之分,次序不能颠倒 ,因此二叉树是 有序树二叉树的结构
二叉树的五种基本形式:![]()
特殊的二叉树
满二叉树
<1>满二叉树:一个二叉树,每层的结点数都达到最大值,则这个二叉树就是满二叉树
假设一颗满二叉树的高度为h
则总节点的个数:
N =
h =
每一个层的结点数都达到最大值 如果一个二叉树的层数为 K ,且结点总数是 2^k-1
完全二叉树
<2>完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为 K 的,有 n 个结点的二叉树,当且仅当其每一个结点都与深度为 K 的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树
假设一颗完全二叉树的高度为h,高度为h的结点个数为x
则总节点的个数:
N =
h =
假设树的高度是h,前 h-1 层是 满 的 最后一行不满,但 从左往右是连续 的
如上图所示就 非 完全二叉树:深度为 K 的节点中,从首结点到末结点中有 “ 缺口 ”
二叉树的性质
<1>若规定根节点的层数为 1 ,则一棵非空二叉树的 第i层上最多有个结点
<2>若规定根节点的层数为 1 ,则 深度为 h 的二叉树的最大结点数是![]()
<3>对任何一棵二叉树 , 如果度为 0 其叶结点个数为n0 , 度为 2 的分支结点个数为n2, 则有 n0=n2+1 <4>若规定根节点的层数为 1 ,具有n个结点的满二叉树的深度![]()
<5>对于具有 n 个结点的完全二叉树,如果按照从上至下从左至右的数组顺序对所有节点从 0 开始编号,则对于序号为 i 的结点有:(1)若i>0,i位置节点的双亲序号:(i-1)/2;i=0,i为根节点编号,无双亲节点 (2)若2i+1<n,左孩子序号:2i+1,2i+1>=n否则无左孩子 (3)若2i+2<n,右孩子序号:2i+2,2i+2>=n否则无右孩子
二叉树的存储结构
二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构
顺序存储
顺序结构存储就是使用 数组来存储 ,一般使用数组 只适合表示完全二叉树 ,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储。 二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树
链式存储
二叉树的链式存储结构是指,用 链表 来表示一棵二叉树,即用 链来指示元素的逻辑关系 。 通常的方法是链表中每个结点由 三个域 组成, 数据域和左右指针域 , 左右指针 分别用来给出该结点 左孩子和右孩子所在的链结点的存储地址 。链式结构又分为 二叉链和三叉链二叉链typedef char BTDataType; typedef struct BinaryTreeNode { struct BinaryTreeNode* left; //指向当前节点左孩子 struct BinaryTreeNode* right; //指向当前节点右孩子 BTDataType data; //节点中的数据域 }BTNode;三叉链typedef char BTDataType; typedef struct BinaryTreeNode { struct BinaryTreeNode* Parent; //指向当前节点的双亲 struct BinaryTreeNode* left; //指向当前节点左孩子 struct BinaryTreeNode* right; //指向当前节点右孩子 BTDataType data; //节点中的数据域 }BTNode;
二叉树的遍历
二叉树遍历 (Traversal): 按照某种特定的规则,依次对二叉 树中的节点进行相应的操作,并且每个节点只操作一次 。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础
按照规则,二叉树的遍历有: 前序 / 中序 / 后序的递归结构遍历<1> 前序遍历 —— 访问根结点的操作发生在遍历其 左右子树之前<2>中序遍历 —— 访问根结点的操作发生在遍历其 左右子树之中<3>后序遍历 —— 访问根结点的操作发生在遍历其 左右子树之后
由于被访问的结点必是某子树的根
所以 N(Node )、 L(Left subtree )和 R(Right subtree)又可解释为 根、根的左子树和根的右子树NLR 、 LNR 和 LRN 分别又称为先根遍历、中根遍历和后根遍历
二叉树结构定义
typedef char BTDataType; typedef struct BinaryTreeNode { struct BinaryTreeNode* left; // 第一个孩子结点 struct BinaryTreeNode* right; // 指向其下一个兄弟结点 BTDataType data; // 结点中的数据域 }BTNode;


二叉树前序遍历
因为有些节点为空,我们可以选择打印和不打印,为了页面美观我们这里就不打印了
void PrevOrder(BTNode* root) { if (root == NULL) { return; } printf("%c ", root->data); PrevOrder(root->left); PrevOrder(root->right); }int main() { BTNode* A = (BTNode*)malloc(sizeof(BTNode)); A->data = 'A'; A->left = NULL; A->right = NULL; BTNode* B = (BTNode*)malloc(sizeof(BTNode)); B->data = 'B'; B->left = NULL; B->right = NULL; BTNode* C = (BTNode*)malloc(sizeof(BTNode)); C->data = 'C'; C->left = NULL; C->right = NULL; BTNode* D = (BTNode*)malloc(sizeof(BTNode)); D->data = 'D'; D->left = NULL; D->right = NULL; BTNode* E = (BTNode*)malloc(sizeof(BTNode)); E->data = 'E'; E->left = NULL; E->right = NULL; BTNode* F = (BTNode*)malloc(sizeof(BTNode)); F->data = 'F'; F->left = NULL; F->right = NULL; BTNode* G = (BTNode*)malloc(sizeof(BTNode)); G->data = 'G'; G->left = NULL; G->right = NULL; BTNode* H = (BTNode*)malloc(sizeof(BTNode)); H->data = 'H'; H->left = NULL; H->right = NULL; BTNode* I = (BTNode*)malloc(sizeof(BTNode)); I->data = 'I'; I->left = NULL; I->right = NULL; BTNode* J = (BTNode*)malloc(sizeof(BTNode)); J->data = 'J'; J->left = NULL; J->right = NULL; BTNode* K = (BTNode*)malloc(sizeof(BTNode)); K->data = 'K'; K->left = NULL; K->right = NULL; A->left = B; A->right = C; B->left = D; B->right = E; D->left = H; D->right = I; C->left = F; C->right = G; F->left = K; E->right = J; PrevOrder(A); printf("\n"); system("pause"); return 0; }以上我们插入树节点数据
代码测试
前序遍历结果:A B D H I E J C F K G
二叉树中序遍历
void InOrder(BTNode* root) { if (root == NULL) { return; } InOrder(root->left); printf("%c ", root->data); InOrder(root->right); }代码测试
中序遍历结果:H D I B E J A F K C G
二叉树后序遍历
void PostOrder(BTNode* root) { if (root == NULL) { return; } PostOrder(root->left); PostOrder(root->right); printf("%c ", root->data); }代码测试
后序遍历结果:H I D J E B K F G C A
二叉树遍历口诀
二叉树的层序遍历
层序遍历太简单了,就是按照一层一层的顺序,从左到右写下来就行了
层序遍历结果:A B C D E F G H I J K
层序遍历我们要用到队列,所以我们这里要包一下队列相关的文件
void Levelorder(BTNode* root) { Queue q; QueueInit(&q); //树为空,直接返回 if (root == NULL) { return; } QueuePush(&q, root); //先将根节点入队 while (!QueueEmpty(&q)) { BTNode* front = QueueFront(&q); //出队保存队头并访问 QueuePop(&q); printf("%c ", front->data); if (front->left) //将出队结点的左子树根入队 { QueuePush(&q, front->left); } if (front->right) //将出队结点的右子树根入队 { QueuePush(&q, front->right); } } printf("\n"); QueueDestory(&q); //销毁队列 }
二叉树结点的个数
结点的个数的算法:左子树的结点加上右子树的结点,最后再加上根结点
int TreeSize(BTNode* root) { return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1; }代码测试
二叉树叶子结点的个数
叶子结点的特征:左右子树为空,我们可以通过递归的方法遍历每一颗子树
int TreeLeafSizee(BTNode* root) { if (root == NULL) return 0; //左右为空 if (root->left == NULL && root->right == NULL) return 1; return TreeLeafSizee(root->left) + TreeLeafSizee(root->right); }我们还是以这个树为例
我们发现有H、I、J、K、G五个左右子树为空,所以叶子结点的个数为5
代码测试
二叉树的高度
树的高度的定义:从 根开始定义 起,根为第一层,根的孩子为第二层,以此类推
int TreeHeight(BTNode* root) { if (root == NULL) return 0; int left = TreeHeight(root->left); int right = TreeHeight(root->right); return (left > right ? left : right) + 1; }代码测试
二叉树第k层节点个数
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); }代码测试
如图所示:4层的结点为4,2层的节点为2
二叉树查找值为x的节点
先对左子树递归查找,如果未找到x,则返回NULL
如果找到x,便返回x所在节点
根据返回值判断是否需要进行右递归查找操作
BTNode* BinaryTreeFind(BTNode* root, BTDataType x) { if (root == NULL) return NULL; if (root->data == x) return root; if (BinaryTreeFind(root->left, x)) return BinaryTreeFind(root->left, x); else return BinaryTreeFind(root->right, x); }
总结:
二叉树主要涉及的算法有 递归 和 分治
递归需要画图理解其真谛



































36万+

被折叠的 条评论
为什么被折叠?



