各位大佬大家好,我是猪皮兄弟
今天带来的内容是二叉树
这里是下面要讲的知识内容
文章目录
- 一、⚽二叉树概念
- 二、⚽树的结构体定义
- 1.用data来存当前结点的值,然后用一个顺序表来存孩子结点的指针
- 2.树的最优表示法-左孩子右兄弟表示法
- 三、⚽二叉树的结构
- 四、⚽二叉树的存储结构
- 1、顺序存储
- 2、链式存储
- 五、⚽三种最常见的遍历方式(递归)
- 1.前序遍历
- 2.中序遍历
- 3.后序遍历
- 六、⚽二叉树求结点树,高度等等...
- 1.求二叉树的size
- 2.求二叉树叶子数
- 3.求k层的节点数
- 4.找某个结点
- 5.求树的高度
- 6.层序遍历,判断是否为完全二叉树
- 七、⚽总结
1.结点的度:一个结点含有子树 的个数称为结点的度
2.叶结点:结点的度为0的点就是叶子结点
3.分支节点:结点的度不为0的结点
4.父结点:若一个结点含有子节点,那么这个结点就是该子节点的父结点
5.子结点
6.兄弟结点:拥有同一个父节点
7.树的度:一个树中的结点拥有的最大的度称为树的度,二叉树树的度最大为2。
8.结点的层次:从根开始定义起,根为第一层,根的子节点为第二层,以此类推
9.树的高度或深度:叶结点的最大层次 ,认为从1开始是最合理的,因为空树深度为0
10.结点的祖先:从根到该节点所经分支上的所有结点
11.子孙:以某结点为根的子树中任意结点都称为该节点的子孙
12.由m棵互不相交的树的集合称为森林
树在现实生活当中其实就是一个文件系统,树的结构体定义可以有两种方式
//struct TreeNode
//{
// int data;
// struct TreeNode**childArray;
// int sz;
// int capacity;
//}
//
typedef struct TreeNode*SLDataType;
struct TreeNode
{
int data;
SeqList _child;//可以实现动态增长的
}
typedef int DataType;
struct TreeNode
{
DataType data;
struct TreeNode* _firstChild;
struct TreeNode* _pNextBrother;
}
1.二叉树每个结点的度而0或1或2,不存在大于2的结点
2.二叉树的子树有左子树和右子树之分,不能颠倒,二叉树是有序树
3.满二叉树,每一层都是满的
4.完全二叉树,除最后一层外都是满的,最后一层从左到右必须是连续的,如果不连续就不是完全二叉树
因为如果是用数组来存储,不管这颗二叉树是什么样的,都需要按完全二叉树的形式去开辟空间存储,这就有可能造成很多的浪费,最典型的就是只有一条分支走到底,而堆就是一颗完全二叉树,很适合用数组去存储,所以,普通的二叉树我们用链表存储(二叉链,三叉链)
因为需要去表示出父节点和子节点的关系,所以必须要这样,用数组去存储最大的优势就是能算出父节点与子节点的位置关系
二叉树的链式存储结构是指,用链表来表示一颗二叉树,也就是用链来指示元素的逻辑关系,通常的方法是链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子的存储地址。现在我们用的是二叉树,比如红黑树等高阶数据结构会用到三叉树
三种遍历方式:
1.前序遍历(Preorder Traversal):访问根结点的操作发生在遍历其左右子树之前
2.中序遍历(Inorder Traversal):访问根结点的操作发生在遍历其左右子树之中
3.后序遍历(Postorder Traversal):访问根结点的操作发生在遍历其左右子树之后
//前序遍历
void preorder(struct TreeNode*root)
{
if(root==NULL)
{
return ;
}
printf("%d",root->data);
preorder(root->lchild);
preorder(root->rchild);
}
//中序遍历
void inorder(struct TreeNode*root)
{
if(root==NULL)
{
return;
}
inorder(root->lchild);
printf("%d",root->data);
inorder(root->rchild);
}
//后序遍历
void torder(struct TreeNode*root)
{
if(root==NULL)
{
return;
}
torder(root->lchild);
torder(root->rchild);
printf("%d",root->data);
}
分而治之,大问题化成小问题
int TreeSize(struct TreeNode*root)
{
if(root==NULL)
return 0;
return TreeSize(root->left)+TreeSize(root->right)+1;
//也可以用三目
//return root==NULL ? 0 : TreeSize(root->left)+TreeSize(root->right)+1;
}
int TreeLeafSize(struct TreeNode*root)
{
if(root==NULL)
return 0;
if(root->left==NULL&&root->right==NULL)
return 1;
return TreeLeafSize(root->left)+TreeLeafSize(root->right);
}
int TreeKLevel(struct TreeNode*root,int k)
{
assert(k>=1);//断言,k必须>=1;
if(root==NULL)
return 0;
if(k==1)
return 1;
return TreeKLevel(root->left,k-1)+ TreeKLevel(root->right,k-1);
}
TreeNode*TreeFind(struct TreeNode*root,DataType x)
{
if(root==NULL)
return NULL;
if(root->val==x)
return root;
struct TreeNode*ret1=TreeFind(root->left);
if(ret1)
return ret1;
struct TreeNode*ret2=TreeFind(root->right);
if(ret2)
return ret2;
return NULL;
}
int TreeDepth(struct TreeNode*root)
{
if(root==NULL)
return 0;
int leftHigh=TreeDepth(root->left);
int rightHign=TreeDepth(root->right);
return 1+(leftHigh>rightHign?leftHign:rightHign);
}
从打印的角度如果把NULL也打印出来,完全二叉树一定是NULL后面全是NULL,而不是完全二叉树的话,NULL和有效数据之间是分开的
层序遍历一般借助队列来完成
bool IsTreeComplete(BTNode*root)
{
Queue q;
QueueInit(&q);
if(root!=NULL)
QueuePush(&q,root);
//因为会
while(!QueueEmpty(&q))
{
BTNode*front=QueueFront(&q);
//取队头数据
QueuePop(&q);
if(front)
{
QueuePush(&q,front->left);
QueuePush(&q,front->right);
//子树是NULL就push NULL进去
}
else
{
//遇到NULL之后跳出层序遍历
break;
}
}
//如果后面全是空,则是完全二叉树
//如果后面还有非空,则不是完全二叉树
while(!QueueEmpty(&q))
{
BTNode*front=QueueFront(&q);
QueuePop(&q);
if(front)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
二叉树用递归的地方比较多,递归的思想要好好的想一想,下一篇我们继续学习堆(一种数据结构)