一、树
树
非线性结构、有一个根结点(无前驱结点)、除根结点外其余节点被分为互不相交的集合、子树不能有交集。
相关概念
1、结点的度:一个结点含有的子树的个数称为该结点的度。如上图ROOT的度是3.
2、叶节点或终端结点:度为0的结点。H、I、J、E、G
3、双亲结点:若一个结点含有子结点,则其称为自己子节点的双亲结点。
4、孩子结点:一个结点含有的子树的根节点称为该节点的子节点。
5、兄弟结点:具有相同双亲结点的结点互称为兄弟结点。
6、树的度:一棵树中,最大的结点的度称为树的度。
7、结点的层次:从根开始,根为第一层以此类推。
8、树的高度或者深度:树中结点的最大层次
9、堂兄弟结点:双亲在同一层的结点互称为堂兄弟结点
10、森林:由大于零棵互不相交的树组成的集合称为森林。
树的表示
树表示起来,既要保存当前结点的值,还要保存结点与结点之间的关系。所以就有着很多的表示方法如:双亲表示法、孩子表示法、孩子双亲表示法、孩子兄弟表示法等。其中最常用的是孩子兄弟表示法(左孩子右兄弟);
如下图即为孩子兄弟表示法
二、二叉树
概念
或者为空或者由一个根结点加上两颗分别为左子树和右子树的二叉树组成,其不存在度大于2的结点,且二叉树的子树有左右之分,次序是不能颠倒的,因此二叉树是有序树。
特殊的二叉树
满二叉树
如果一个二叉树每一层的结点数都达到了最大值,则称为满二叉树,即如果一个满二叉树有K层,则满二叉树的结点总数为。
完全二叉树
完全二叉树是由满二叉树引出来的,对于深度为K,结点数为n的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号1到n的结点一一对应,则称其为完全二叉树。完全二叉树是一种存储效率很高的数据结构。
二叉树的性质
1、对于任意一棵二叉树,如果度为0的结点的个数为n,度为2的结点数为k,则有n=k+1
2、对于一棵有着n个结点的完全二叉树,如果按照从上向下从左向右对所有结点从0开始编号,则对于序号为i的结点有:
如果其有双亲结点,则其双亲结点的编号为(i-1)/2;
如果其有孩子结点,则其左孩子结点的编号为2i+1;
二叉树的存储
顺序存储
顺序存储就是利用数组来存储,二叉树顺序存储在物理上是一个数组,在逻辑上是一棵树。但是顺序存储只适合表示完全二叉树,因为非完全二叉树会造成空间浪费。现实中只有堆才会使用数组来存储。
链式存储
链式存储即用链表来表示一颗二叉树,分为二叉链和三叉链。
二叉树二叉链的简单实现
二叉树的结点需要包含三个域,保存该结点数据的域,保存指向该结点的左孩子和右孩子的指针。
typedef struct TreeNode
{
BTDataType _val;
struct TreeNode* _left;
struct TreeNode* _right;
}TreeNode;
建树结点
BTNode* BuyBinaryTree(BTDataType x)//创建一个树的节点
{
BTNode* BT = (BTNode*)malloc(sizeof(BTNode));
if (BT == NULL)
{
printf("申请节点失败\n");
exit(-1);
}
BT->_data = x;
BT->_left = NULL;
BT->_right = NULL;
return BT;
}
树的遍历
树的遍历我们用树的递归性质来做。
void PreOrder(BTNode* root)//前序遍历
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%c ", root->_data);
PreOrder(root->_left);
PreOrder(root->_right);
}
void InOrder(BTNode* root)//中序遍历
{
if (root == NULL)
{
printf("NULL ");
return;
}
InOrder(root->_left);
printf("%c ", root->_data);
InOrder(root->_right);
}
void PostOrder(BTNode* root)//后序遍历
{
if (root == NULL)
{
printf("NULL ");
return;
}
PostOrder(root->_left);
PostOrder(root->_right);
printf("%c ", root->_data);
}
树的层序遍历
其实树的层序遍历就是将一颗树从上到下,从左到右按顺序依次打印,这里我们可以利用队列的先进先出的特点。
首先判断根结点是否为空,不为空时将根结点压栈,利用循环不断取出栈顶元素进行出栈操作(注意此时并不是将其删除,只是将其从队列里面拿掉),再打印当前结点的数据。后判断是否有孩子结点,如果有则依次压栈。根据队列先进先出的特点,我们就可以层序遍历打印树的每一个结点。
void BinaryTreeLevelOrder(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);
}
}
QueueDestroy(&q);
printf("\n");
}
判断当前二叉树是否是完全二叉树
对于非完全二叉树来说,进行层序遍历之后,会出现NULL结点之后还会出现非NULL结点的情况,而对于完全二叉树来说,在空结点NULL之后全是NULL,利用这个特点我们对树进行层序遍历,遇到NULL就跳出循环,对队列中剩下的元素判断是否出现NULL。
int BinaryTreeComplete(BTNode* root)//判断是否为完全二叉树,是则返回1,否则返回0,
{
//根据完全二叉树的性质特点,进行层序遍历遇到NULL跳出循环,若接下来遍历中遇到的全是NULL,则是完全二叉树
Queue q;
QueueInit(&q);
if (root == NULL)
return 0;
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)//层序遍历,遇到第一个NULL跳出。
{
break;
}
QueuePush(&q, front->_left);
QueuePush(&q, front->_right);
}
while (!QueueEmpty(&q))//在后面序列中继续判断是否有非空
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front)
{
QueueDestroy(&q);//销毁
return 0;
}
}
QueueDestroy(&q);
return 1;
}
求树结点的个数
int SizeBinaryTree(BTNode* root)//求树的大小,即节点的个数
{
if (root == NULL)
return 0;
else
return 1 + SizeBinaryTree(root->_left) + SizeBinaryTree(root->_right);
}
树的叶子个数
int SizeLeafTree(BTNode* root)//求树的叶子的个数
{
if (root == NULL)
return 0;
else if (root->_left == NULL && root->_right == NULL)
return 1;
else
return SizeLeafTree(root->_left) + SizeLeafTree(root->_right);
}
树的深度
int MaxDepthTree(BTNode* root)//求树的深度
{
if (root == NULL)
{
return 0;
}
else
{
int Ldep = MaxDepthTree(root->_left);
int Rdep = MaxDepthTree(root->_right);
return 1 + ((Ldep >= Rdep) ? Ldep : Rdep);
}
}
二叉树第K层结点的个数
int BinaryTreeLevelKSize(BTNode* root,int k)//二叉树第K层节点的个数
{
if (root == NULL)
return 0;
if (k == 1)
return 1;
else
return BinaryTreeLevelKSize(root->_left, k - 1) + BinaryTreeLevelKSize(root->_right, k - 1);
}
查找值为x的结点,返回其结点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)//二叉树查找值为X的节点,并能返回相对应的节点
{
if (root == NULL)
{
return NULL;
}
if (root->_data == x)
{
return root;
}
BTNode* node = BinaryTreeFind(root->_left, x);//在左子树找,需要记录当前节点,对应则返回
if (node)
return node;
node = BinaryTreeFind(root->_right, x);//在右子树找
if (node)
return node;
return NULL;//找不到
}
销毁二叉树
void BinaryTreeDestroy(BTNode* root)//销毁二叉树,后序遍历销毁,不然先销毁头部可能会找不到子树
{
if (root == NULL)
return;
BinaryTreeDestroy(root->_left);
BinaryTreeDestroy(root->_right);
free(root);
}
运行结果
BTNode* CreatBinaryTree()//创建树
{
BTNode* A = BuyBinaryTree('A');
BTNode* B = BuyBinaryTree('B');
BTNode* C = BuyBinaryTree('C');
BTNode* D = BuyBinaryTree('D');
BTNode* E = BuyBinaryTree('E');
BTNode* F = BuyBinaryTree('F');
A->_left = B;
A->_right = C;
B->_left = D;
B->_right = E;
C->_right = F;
return A;
}
int main()
{
BTNode* root = CreatBinaryTree();
PreOrder(root);
printf("\n");
InOrder(root);
printf("\n");
PostOrder(root);
printf("\n");
int Tsz = SizeBinaryTree(root);
printf("BinaryTree:%d\n", Tsz);
int Lsz = SizeLeafTree(root);
printf("LeafTree:%d\n", Lsz);
int depth = MaxDepthTree(root);
printf("DepthTree:%d\n", depth);
int LevelK = BinaryTreeLevelKSize(root, 3);
printf("BinaryTreeLevelKSize:%d\n", LevelK);
BinaryTreeLevelOrder(root);
int Btc=BinaryTreeComplete(root);
printf("BinaryTreeComplete:%d\n", Btc);
return 0;
}