一、树的定义和性质
(1)树的定义:树是n(n>=0)个结点的有限集。
- 当n=0时该树称为空树。
- 当n>0时,仅有一个称为根的结点。同时其余节点可分为m(m>0)个互不相交的有限集T1,T2,…,Tm,其中每个集合本身又是一棵树,并且称为根的子树。
结点的度:一个结点拥有的子树个数称为该结点的度。
叶节点(终端结点):度为0的结点称为叶节点。
分支结点:度不为0的结点。
父结点:当一个结点有子结点,则称这个结点为这个子结点的父结点。
子结点:若一个结点含有的子树的根结点,则称该结点的子结点。
兄弟结点:拥有相同的父结点称为兄弟结点。
树的度:在一棵树中,最大的结点的度被称为树的度。
结点的层次:定义上来说根结点层次为1,根节点的子结点层次为2,如此类推。
树的高度:树中结点的最大层次。
堂兄弟的节点:父节点在同一层的节点称为堂兄弟结点。
节点的祖先:从根结点到该结点所经过分支的所有结点,被称为该结点的祖先(包括根结点)。
子孙:以某结点为根的子树中任意一结点都称为该结点的子孙。
森林:由n(n>0)棵不相交的树组成的集合称为森林。
(2)树的性质
- 树中结点数 = 所有结点的度+1。
- 度为m的树中第i层最多有m^(i-1)个结点。
- 高度为h的m叉树最多有(m^h - 1)/ (m - 1)个结点。
- 有n个结点的m叉树最小高度为logm (n(m-1)+1)
(3)树与非树
- 子树不相交。
- 除了根结点外每个结点都有且仅有一个父结点。
- 一棵n个结点的树,有n-1条度。
二、二叉树
二叉树是一种特殊的树,度最大为2。
二叉树有左右子树之分,次序不能颠倒,所以二叉树的有序树。
满二叉树
当一个二叉树每一层的结点都达到饱和,我们称这个二叉树为满二叉树。(如图)
所以,一个满h层的二叉树有2^h - 1个结点。
完全二叉树
完全二叉树除了最后一层外,其余层必须饱和。最后一层可以不饱和(也可以饱和),但必须遵循从左到右连续。
注:满二叉树是特殊的完全二叉树。
完全二叉树:
非完全二叉树:
没有遵循从左到右连续的原则。
所以当完全二叉树的高为h,那么他有[2^(h-1),2^h - 1]个结点。
二叉树的遍历
- 二叉树的前序遍历:根->左->右
- 二叉树的中序遍历:左->根->右
- 二叉树的后序遍历:左->右->根
- 二叉树的层序遍历:第一层->第二层->...->第h层
三、二叉树的代码实现
二叉树的定义
typedef int BTDataType; //便于修改要存储的数据
typedef struct BinaryTreeNode
{
BTDataType data; //存储数据
struct BinaryTreeNode* left;//左孩子(左子树根结点)
struct BinaryTreeNode* right;//右孩子(右子树根结点)
}BTNode;
二叉树的构建
以数组"ABD##E#H##CF##G##"为例,构建二叉树。
// 通过前序遍历的数组"ABD##E#H##CF##G##"构建二叉树
// pi记录数组a使用到哪一个元素
BTNode* BinaryTreeCreate(BTDataType* a, int* pi)
{
assert(a && pi);
if (a[*pi] == '#')
{
(*pi)++;
return NULL;
}
BTNode* newnode = (BTNode*)malloc(sizeof(BTNode));
if (newnode == NULL)
{
perror("malloc failure");
exit(-1);
}
newnode->data = a[(*pi)++];
newnode->left = BinaryTreeCreate(a, pi);
newnode->right = BinaryTreeCreate(a, pi);
return newnode;
}
二叉树销毁
void BinaryTreeDestory(BTNode** root)
{
assert(root);
if (*root == NULL)
return;
BinaryTreeDestory(&((*root)->left));
BinaryTreeDestory(&((*root)->right));
free(*root);
*root = NULL;
}
二叉树节点个数
int BinaryTreeSize(BTNode* root)
{
if (root == NULL)
return 0;
return BinaryTreeSize(root->left) + BinaryTreeSize(root->right) + 1;
}
二叉树叶子节点个数
int BinaryTreeLeafSize(BTNode* root)
{
if (root == NULL)
return 0;
if (root->left == NULL && root->right == NULL)
return 1;
return BinaryTreeLeafSize(root->right)+BinaryTreeLeafSize(root->left);
}
二叉树第k层节点个数
int BinaryTreeLevelKSize(BTNode* root, int k)
{
assert(k > 0);
if (root == NULL)
return 0;
if (k == 1)
return 1;
return BinaryTreeLevelKSize(root->left, k - 1) + BinaryTreeLevelKSize(root->right, k - 1);
}
二叉树查找值为x的节点
BTNode* BinaryTreeFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* ret = NULL;
if (ret = BinaryTreeFind(root->left, x))
return ret;
if (ret = BinaryTreeFind(root->right, x))
return ret;
return NULL;
}
二叉树遍历
层序遍历是利用队列先进先出的特点来实现的,先将根结点入队,然后只要队列不为空就先访问访问队列中第一个元素,再依次去访问左右结点(根结点是第一层,他的左右结点是第二层,然后如此类推就是第三层)
//前序遍历
void BinaryTreePrevOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
printf("%c ", root->data);
BinaryTreePrevOrder(root->left);
BinaryTreePrevOrder(root->right);
}
//中序遍历
void BinaryTreeInOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
BinaryTreeInOrder(root->left);
printf("%c ", root->data);
BinaryTreeInOrder(root->right);
}
//后序遍历
void BinaryTreePostOrder(BTNode* root)
{
if (root == NULL)
{
printf("N ");
return;
}
BinaryTreePostOrder(root->left);
BinaryTreePostOrder(root->right);
printf("%c ", root->data);
}
//层序遍历
void BinaryTreeLevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root != NULL)
{
QueuePush(&q, root);
}
while (QueueEmpty(&q) == 0)
{
printf("%c ", QueueFront(&q)->data);
if (QueueFront(&q)->left != NULL)
{
QueuePush(&q, QueueFront(&q)->left);
}
if (QueueFront(&q)->right != NULL)
{
QueuePush(&q, QueueFront(&q)->right);
}
QueuePop(&q);
}
}
判断二叉树是否是完全二叉树
bool BinaryTreeComplete(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
int levelSize = 1;
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
if (front == NULL)
break;
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;
}