二叉树概念及结构
一棵二叉树是结点的一个有限集合,该集合:
- 或者为空
- 由一个根节点加上两棵别称为左子树和右子树的二叉树组成
从上图可以看出:
- 二叉树不存在度大于2的结点
- 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树
注意:对于任意的二叉树都是由以下几种情况复合而成的:
从概念中可以看出,二叉树定义是递归式
的,因此后序基本操作中基本都是按照该概念实现的。
二叉树的遍历
所谓二叉树遍历(Traversal)是按照某种特定的规则,依次对二叉树中的节点进行相应的操作,并且每个节点只操作一次。访问结点所做的操作依赖于具体的应用问题。 遍历是二叉树上最重要的运算之一,也是二叉树上进行其它运算的基础。层序因为不是递归结构所以就放在后面了。
二叉树的遍历有:前序/中序/后序的递归结构遍历
:
- 前序遍历(Preorder Traversal 亦称先序遍历)——访问根结点的操作发生在遍历其左右子树之前。
- 中序遍历(Inorder Traversal)——访问根结点的操作发生在遍历其左右子树之中(间)。
- 后序遍历(Postorder Traversal)——访问根结点的操作发生在遍历其左右子树之后。
最后的结果:
前序:1 2 3 4 5 6
中序:3 2 1 5 4 6
后序:3 2 5 6 4 1
二叉树链式结构的实现
实现二叉树链式结构主要是应用
递归
的思想,并且这里二叉树的增删查改没有有意义。 在学习二叉树的基本操作前,需先要创建一棵二叉树,然后才能学习其相关的基本操作。由于现在大家对二叉树结构掌握还不够深入,为了降低大家学习成本,此处手动快速创建一棵简单的二叉树,快速进入二叉树操作学习,等二叉树结构了解的差不多时,我们反过头再来研究二叉树真正的创建方式。
创建二叉树
首先创建个二叉树方便对各功能的检测,下面的图片也就是代码创建的二叉树。
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
//创建节点
BTNode* BuyBTNode(BTDataType x)
{
BTNode* tmp = (BTNode*)malloc(sizeof(BTNode));
if (tmp == NULL)
{
perror("malloc");
exit(-1);
}
tmp->data = x;
tmp->left = NULL;
tmp->right = NULL;
return tmp;
}
int main()
{
//构建二叉树
BTNode* node1 = BuyBTNode(1);
BTNode* node2 = BuyBTNode(2);
BTNode* node3 = BuyBTNode(3);
BTNode* node4 = BuyBTNode(4);
BTNode* node5 = BuyBTNode(5);
BTNode* node6 = BuyBTNode(6);
node1->left = node2;
node1->right = node4;
node2->left = node3;
node4->left = node5;
node4->right = node6;
}
二叉树遍历
下图为前序遍历图解:
下图为前序完整遍历代码的结构图:
//前序遍历
void PrveNode(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%d ", root->data);
PrveNode(root->left);
PrveNode(root->right);
}
//中序遍历
void InNode(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
InNode(root->left);
printf("%d ", root->data);
InNode(root->right);
}
//后序遍历
void PostNode(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostNode(root->left);
PostNode(root->right);
printf("%d ", root->data);
}
下图为打印结果:
二叉树节点数
//结点数
//方法一
int size = 0;
void TreeSize1(BTNode* root)
{
if (root == NULL)
{
return;
}
size++;
TreeSize1(root->left);
TreeSize1(root->right);
}
//方法二
int TreeSize2(BTNode* root)
{
if (root == NULL)
{
return 0;
}
return TreeSize2(root->left) + TreeSize2(root->right) + 1;
}
二叉树叶子节点数
//叶子节点个数
int LeafSize(BTNode* root)
{
if (root == NULL)
{
return 0;
}
if (root->left == NULL && root->right == NULL)
{
return 1;
}
return LeafSize(root->left) + LeafSize(root->right);
}
二叉树的高度
//树的高度
int TreeHeigth(BTNode* root)
{
if (root == NULL)
{
return 0;
}
int leftheight = TreeHeigth(root->left);
int rigthheight = TreeHeigth(root->right);
return leftheight > rigthheight ? leftheight + 1 : rigthheight + 1;
}
二叉树第k层的节点个数
//求k层的节点数//k>=0
int TreeKsize(BTNode* root, int k)
{
if (root == NULL)
{
return 0;
}
if (k == 1)
{
return 1;
}
return TreeKsize(root->left, k - 1) + TreeKsize(root->right, k - 1);
}
二叉树结点x的地址
//求节点X的位置
BTNode* TreePlace(BTNode* root, int x)
{
if (root == NULL)
{
return 0;
}
if (root->data == x)
{
return root;
}
BTNode* ret1 = TreePlace(root->left, x);
if (ret1)
{
return ret1;
}
BTNode* ret2 = TreePlace(root->right, x);
if (ret2)
{
return ret2;
}
}
二叉树的销毁
销毁也并不能从上至下的方式来销毁,这里要使用递归方法来销毁二叉树。
利用后序的思想
//销毁二叉树
//后序的思想
void TreeDestory(BTNode* root)
{
if (root==NULL)
{
return;
}
TreeDestory(root->left);
TreeDestory(root->right);
free(root);
}
层序遍历
这也是属于二叉树的遍历的一种,层序并不是由递归而产生的,用于来解答一些特殊的问题(判断二叉树是否是完全二叉树)。除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。
下面为图解:
代码如下:
这里我们需要调用一下之前创建好的队列,需要将二叉树的结点值以层序的方式来存放在队列当中。
//层序遍历
void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q, root);
}
while (!QueueEmpty(&q))
{
BTNode* Front = QueueFront(&q);
printf("%d ",Front->data);
QueuePop(&q);
if (Front->left)
{
QueuePush(&q,Front->left);
}
if (Front->right)
{
QueuePush(&q, Front->right);
}
}
printf("\n");
QueueDestroy(&q);
}
判断二叉树是否是完全二叉树
在这题当中就很好的应用了层序遍历的思想,完全二叉树的特点是结点是连续的,在最后一层当中不会出现结点终端的现象。并不能用节点数量来判断是不是完全二叉树,节点数量可以用来判断是不是满二叉树。
思想:就是用层序遍历二叉树直到找到为空的节点,跳出来在判断后面还有没有不为空的节点。
如图:
代码:
//判断二叉树是不是完全二叉树
bool TreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
{
QueuePush(&q,root);
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front==NULL)
{
break;
}
else
{
QueuePush(&q, front->left);
QueuePush(&q, front->right);
}
}
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (!front)
{
QueueDestroy(&q);
return false;
}
}
QueueDestroy(&q);
return true;
}
最后:文章有什么不对的地方或者有什么更好的写法欢迎大家在评论区指出 |