数据结构——树,二叉树
树:
树是⼀种非线性的数据结构,它是由 n(n>=0)个有限结点组成⼀个具有层次关系的集合。
树中有⼀个特殊的结点,称为根结点,根结点没有前驱结点。除根结点外,其余结点被分成 M(M>0) 个互不相交的集合 T1、T2、……、Tm ,其中每⼀个集合 Ti(1 <= i <= m) ⼜是⼀棵结构与树类似的⼦树。每棵⼦树的根结点有且只有⼀个前驱,可以 有 0 个或多个后继。因此,树是递归定义的。 树形结构中,子树之间不能有交集,否则就不是树形结构。
树中的相关术语:
⽗结点/双亲结点:若⼀个结点含有⼦结点,则这个结点称为其⼦结点的⽗结点;
⼦结点/孩⼦结点:⼀个结点含有的⼦树的根结点称为该结点的⼦结点;
结点的度:⼀个结点有⼏个孩⼦,他的度就是多少;
树的度:⼀棵树中,最⼤的结点的度称为树的度;
叶⼦结点/终端结点:度为 0 的结点称为叶结点;
分⽀结点/⾮终端结点:度不为 0 的结点;
兄弟结点:具有相同⽗结点的结点互称为兄弟结点(亲兄弟);
结点的层次:从根开始定义起,根为第 1 层,根的⼦结点为第 2 层,以此类推;
树的⾼度或深度:树中结点的最⼤层次;
结点的祖先:从根到该结点所经分⽀上的所有结点
路径:⼀条从树中任意节点出发,沿⽗节点-⼦节点连接,达到任意节点的序列
⼦孙:以某结点为根的⼦树中任⼀结点都称为该结点的⼦孙。
森林:由 m(m>0) 棵互不相交的树的集合称为森林;
在树形结构中,我们最常⽤的就是⼆叉树,⼀棵⼆叉树是结点的⼀个有限集合,该集合由⼀个根结点 加上两棵别称为左⼦树和右⼦树的⼆叉树组成或者为空。
二叉树:
在树形结构中,我们最常⽤的就是⼆叉树,⼀棵⼆叉树是结点的⼀个有限集合,该集合由⼀个根结点 加上两棵别称为左⼦树和右⼦树的⼆叉树组成或者为空。
⼆叉树具备以下特点: 1. ⼆叉树不存在度⼤于 2 的结点 2. ⼆叉树的⼦树有左右之分,次序不能颠倒,因此⼆叉树是有序树。
满二叉树:
⼀个⼆叉树,如果每⼀个层的结点数都达到最⼤值,则这个⼆叉树就是满⼆叉树。也就是说,如果⼀ 个⼆叉树的层数为 K ,且结点总数是 2 ^ k − 1 ,则它就是满⼆叉树。
完全二叉树:
完全⼆叉树是效率很⾼的数据结构,完全⼆叉树是由满⼆叉树⽽引出来的。对于深度为 K 的,有 n 个 结点的⼆叉树,当且仅当其每⼀个结点都与深度为K的满⼆叉树中编号从 1 ⾄ n 的结点⼀⼀对应时称 之为完全⼆叉树。要注意的是满⼆叉树是⼀种特殊的完全⼆叉树。
二叉树的性质:
根据满⼆叉树的特点可知: 1)若规定根结点的层数为 1 ,则⼀棵⾮空⼆叉树的第i层上最多有 2 ^ (i−1) 个结点
2)若规定根结点的层数为 1 ,则深度为 h 的⼆叉树的最⼤结点数是 (2 ^ h) - 1;
3)若规定根结点的层数为 1 ,具有 n 个结点的满⼆叉树的深度为
二叉树的实现:
头文件 Tree.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
typedef char BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
//根据前序遍历构建二叉树
BTNode* BinaryTreeCreate(char* tree, int* pi);
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root);
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root);
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root);
// 二叉树销毁
void BinaryTreeDestory(BTNode** root);
// 二叉树节点个数
int BinaryTreeSize(BTNode* root);
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root);
// 二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k);
//二叉树深度
int BinaryTreeDepth(BTNode* root);
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x);
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root);
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root);
源文件 Tree.c
#include"Tree.h"
#include"queue.h"
BTNode* buyNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror(malloc);
exit(1);
}
node->data = x;
node->left = node->right = NULL;
return node;
}
BTNode* BinaryTreeCreate(char* tree, int* pi)
{
if (tree[*pi] == '#')
{
(*pi)++;
return NULL;
}
BTNode* root = buyNode(tree[*pi]);
(*pi)++;
root->left = BinaryTreeCreate(tree, pi);
root->right = BinaryTreeCreate(tree, pi);
return root;
}
// 二叉树前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
printf("%c ", root->data);
BinaryTreePrevOrder(root->left);
BinaryTreePrevOrder(root->right);
}
// 二叉树中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
BinaryTreeInOrder(root->left);
printf("%c ", root->data);
BinaryTreeInOrder(root->right);
}
// 二叉树后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
printf("NULL ");
return;
}
BinaryTreePostOrder(root->left);
BinaryTreePostOrder(root->right);
printf("%c ", root->data);
}
// 层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
Queue q;
QInit(&q);
QPush(&q, root);
while (!QEmpty(&q))
{
BTNode* top = QFront(&q);
QPop(&q);
printf("%c ", top->data);
if (top->left)
QPush(&q, top->left);
if (top->right)
QPush(&q, top->right);
}
QDestroy(&q);
}
// 二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
return 0;
return 1 + BinaryTreeSize(root->left) + BinaryTreeSize(root->right);
}
// 二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->left == NULL && root->right == NULL)
return 1;
return BinaryTreeLeafSize(root->left)
+ BinaryTreeLeafSize(root->right);
}
// 二叉树第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);
}
//二叉树深度
int BinaryTreeDepth(BTNode* root)
{
if (root == NULL)
return 0;
int leftd = BinaryTreeDepth(root->left);
int rightd = BinaryTreeDepth(root->right);
return 1 + (leftd > rightd ? leftd : rightd);
}
// 二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* ret = BinaryTreeFind(root->left, x);
if (ret)
return ret;
ret = BinaryTreeFind(root->right, x);
if (ret)
return ret;
return NULL;
}
// 判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
Queue q;
QInit(&q);
QPush(&q, root);
while (!QEmpty(&q))
{
BTNode* top = QFront(&q);
QPop(&q);
if (top == NULL)
break;
QPush(&q, top->left);
QPush(&q, top->right);
}
while (!QEmpty(&q))
{
BTNode* top = QFront(&q);
QPop(&q);
if (top)
{
QDestroy(&q);
return false;
}
}
QDestroy(&q);
return true;
}
// 二叉树销毁
void BinaryTreeDestory(BTNode** proot)
{
if (*proot == NULL)
return;
BinaryTreeDestory(&((*proot)->left));
BinaryTreeDestory(&((*proot)->right));
free(*proot);
*proot = NULL;
}
二叉树的相关函数中大多使用了递归来定义,毕竟树结构也是通过递归来定义的。
二叉树的遍历:
二叉树的先序遍历:先遍历根节点,再遍历左儿子(若有),最后遍历右儿子(若有)。(根左右)
二叉树的中序遍历:先遍历左儿子(若有),再遍历根节点,最后遍历右儿子(若有)。(左根右)
二叉树的后序遍历:先遍历左儿子(若有),再遍历右儿子(若有),最后遍历根节点。 (左右根)
当知道二叉树的前序遍历,后序遍历之一和中序遍历是就可以唯一确定一颗二叉树,但知道前序和后序是无法确定二叉树的。
由中序、后序、前序遍历得到二叉树的一半方法:
- 由前序或者后序确定根节点;
- 在中序遍历中找到根节点的左右子树所对应的区间;
- 再在左右子树中重复1,2过程,最终就可以得到一颗二叉树。