目录
前言
这就是我们现实中的树
一、什么是二叉树?
二叉树是一种树,树有树根,树叶,分支;
树是也是一种非线性的数据结构,它是由n(n>=0)个有限结点组成一个具有层次关系的集合。把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的。
二、数的相关概念
概念过于长,不必全看,只需要看加粗部分
节点的度:一个节点含有的子树的个数称为该节点的度; 如上图: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)棵互不相交的树的集合称为森林;
三、二叉树的实现
1.二叉树的存储结构
二叉树的存储分为顺序表存储和链式存储,两者在栈和队列都有所实现;
顺序表顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺序存储在物理上是一个数组,在逻辑上是一颗二叉树。
链式结构
普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。
四、代码实现
代码实现这棵树
1.代码构建树
用链表实现需要创建节点,也就需要开辟空间存储数据和左右分支地址;
代码如下(示例):
typedef int BTDataType;
typedef struct treenode
{
struct treenode* left;
struct treenode* right;
BTDataType data;
}BTNode;
BTNode* BuyNode(int x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("malloc fail");
exit(-1);
}
node->data = x;
node->left = NULL;
node->right = NULL;
return node;
}
2、 二叉树的链接
开辟完空间要搭建二叉树,就需要将每一个节点个链接起来,形成二叉树;我们链接完将二叉树的根也就是最顶端节点1的地址返回,以便于后续操作;也可将这一步写入main函数
BTNode* LinkNode()
{
BTNode* treenode1 = BuyNode(1);
BTNode* treenode2 = BuyNode(2);
BTNode* treenode3 = BuyNode(3);
BTNode* treenode4 = BuyNode(4);
BTNode* treenode5 = BuyNode(5);
BTNode* treenode6 = BuyNode(6);
BTNode* treenode7 = BuyNode(7);
treenode1->left = treenode2;
treenode1->right = treenode4;
treenode2->left = treenode3;
treenode4->left = treenode5;
treenode5->left = treenode7;
treenode4->right = treenode6;
return treenode1;
}
3.二叉树的遍历
二叉树遍历有三种方式,分别为前中后序,区别在于根的位置;
前序:根、左子树、右子树
中序;左子树、根、右子树
后续:左子树、右子树、根
此二叉树样例讲解
前序:我们进来先找根,从1 的位置进入的,而1也就是根所以最先为1打印;左子树到了2,2是根打印;再左子树到了3,3是根打印;3的左子树为空,打印N,3的右子树为空打印N,返回到2,2的右子树为空打印N,返回到1,1的右子树为4,4是根打印,4的左子树5,打印,5的左子树7,打印,7的左子树为N,打印,右子树为空打印N;返回到5,5的右子树为6打印,6的左右子树都为空打印两个N
最后打印出来的顺序为:1->2->3->N->N->N->4->5->7->N->N->N->6->N->N;
代码如下(示例):
//头序遍历
void PrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%d ", root->data);
PrevOrder(root->left);
PrevOrder(root->right);
}
我们遍历用的是递归的方式;流程图讲解
中后序同理
直接代码实现
//中序遍历
void InOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
//后序遍历
void PostOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
打印结果
如果不懂可以反复看流程图并;
4、二叉树节点个数
只需要将他们全部遍历一遍就可以了;前中后序都可以;
代码如下(示例):
// 二叉树节点个数
int size = 0;
//int TreeSize(BTNode* root)
//{
// if (root == NULL)
// {
// return 0;
// }
// else
// size++;
// TreeSize(root->left);
// TreeSize(root->right);
// return size;
//}
//法2
int TreeSize(BTNode* root)
{
return root == NULL ? 0 : TreeSize(root->left) + TreeSize(root->right) + 1;
}
法二和法一逻辑是一样的;
5、二叉树子叶子节点个数
逻辑为先先找的N节点然后返回0;然后找到左右节点都为空的节点,这个就是叶子节点;然后返回1;将将左右子树的子叶子都饭后到最上面的根时,程序结束;
代码如下(示例):
int TreeLeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return TreeLeafSize(root->left) + TreeLeafSize(root->right);
}
不理解可自行话流程图;
6、二叉树第k层节点个数
子问题:转换为左子树的k-1层和右子树的k-1层;也就是,递归k-1次函数即可;然后将左右子树上第层节点相加即可;
代码如下(示例):
int TreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);
if (root == NULL)
return 0;
if (k == 1)
{
return 1;
}
return TreeLevelKSize(root->left, k - 1)
+ TreeLevelKSize(root->right, k - 1);
}
三、主函数
总结
重点在于遍历;
遍历分为前中后序:
前序:根、左子树、右子树
中序;左子树、根、右子树
后续:左子树、右子树、根