树
树
树的概念
树是一种非线性的数据结构,它是由n(n>=0)个有限结点组成的一个具有树型层次结构的集合。
任何一颗树都是由 根和子树 组成,而子树又是由根和子树组成,因此,树是递归定义的。
【注意】: 1、树形结构中,子树之间不相交,否则就不是树形结构。
2、树中除了根结点外,每个结点都有且仅有一个父结点。
3、一颗 N个结点的树有 N-1 条分支。
树的相关术语
结点的度:一个结点含有的子树个数 称为该结点的度;如上图,A的度为6。
树的度:一棵树中,最大结点的度 称为树的度;如上图:树的度为6。
叶结点(终端结点):度为0的结点;如上图,B、H、P、N等结点为叶结点。
分支结点(非终端结点):度不为0的结点;如上图,A、D、J等结点为分支节点。
父结点(双亲结点):若一个结点含有子结点,则称这个结点为其子结点的父节点;如上图,A是B的父结点。
子结点:本结点称为其直接上层结点的子结点;如上图,B是A的子结点。
兄弟结点:具有相同父结点的结点互称为兄弟结点;如上图,B、C就是兄弟结点。
结点的层次:从根开始定义,根为第1层,根的子结点为第2层,以此类推。(也可为,根为第0层,以此类推)
树的高度(深度):树的最大结点层次;如上图,树的高度为4。
结点的祖先:从根到该结点所经分支上的所有结点;如上图,A是所有结点的祖先。
子孙:以某结点为根的子树中任一结点都称为该结点的子孙;如上图,所有结点都是A的子孙。
森林:由m(m>1)棵互不相交的树组成的集合 称为森林。
二叉树
二叉树的概念及结构
概念:除空树以外,任何一颗二叉树都是由根结点和两颗分别称为左子树和右子树的二叉树组成。
对于任意的二叉树都是由以下几种情况复合而成的:
特殊的二叉树
完全二叉树:一棵深度为 k层的二叉树,前 k-1 层的结点数均达到最大,而最底层(第k层)的结点是从左到右连续排列的,则它就是完全二叉树。
完全二叉树总结点数量范围为:[2^(k-1), 2^k -1]
满二叉树:一颗深度为 k层的二叉树,每一层的结点数都达到了最大值,那么这棵二叉树就是满二叉树。满二叉树是一种特殊的完全二叉树。
满二叉树总结点数为:2^k - 1
二叉树的性质
1、对于任何一颗二叉树,如果度为0的结点个数为N0,度为2的结点个数为N2,则有 N0 = N2 + 1 => 二叉树度为0的节点个数永远比度为2的结点多一个。
2、完全二叉树的 N1(度为1的结点) 要么为1,为1时总结点个数为偶数;要么为0,为0时总结点个数为奇数。
二叉树的顺序结构及实现
二叉树常用接口(链式结构)的实现
二叉树结点的定义
typedef int BTDataType;
typedef struct BinaryTreeNode
{
BTDataType data;
struct BinaryTreeNode* left;
struct BinaryTreeNode* right;
}BTNode;
顺序结构和链式结构的区别在定义时就已经展现,顺序结构直接定义一个
用于指向动态数组的指针,这一个数组便可贯穿整个数据结构的实现;而
链式结构每次最初定义的都是组成链的结点,多个结点连接起来就是链,所
以即使是有特殊要求,链式结构的最初定义还是结点,然后可以按要求对结
点类型进行二次封装,如队列那样,树也符合这种理念。
总结:顺序结构一级定义是对指向动态数组的指针及其描述数组属性的变量封装,链式结构一级定义是对一个结点内容的封装。
创建树结点
BTNode* BuyNode(BTDataType x)
{
BTNode* node = (BTNode*)malloc(sizeof(BTNode));
if (node == NULL)
{
perror("malloc fail");
return;
}
node->data = x;
node->left = NULL;
node->right = NULL;
return node;
}
前序遍历
前序遍历 — 根,左子树,右子树
void PreOrder(BTNode* root)
{
if (root == NULL)
return NULL;
printf("%d ", root->data);
PreOrder(root->left);
PreOrder(root->right);
}
中序遍历
中序遍历 — 左子树,根,右子树
void InOrder(BTNode* root)
{
if (root == NULL)
return NULL;
InOrder(root->left);
printf("%d ", root->data);
InOrder(root->right);
}
后序遍历
后序遍历 — 左子树,右子树,根
void PostOrder(BTNode* root)
{
if (root == NULL)
return NULL;
PostOrder(root->left);
PostOrder(root->right);
printf("%d ", root->data);
}
查看二叉树的结点个数
思路:递归到最深层,只要不为空,每返回依次记一次数,最终返回到最外层时便可得到结点总数。
int TreeSize(BTNode* root)
{
if (root == NULL)
return 0;
return TreeSize(root->left) + TreeSize(root->right) + 1;
}
运用了分治的方法:将计算总结点数分为计算左结点数和计算右节点数两块来得到,最后加上一个根结点数就OK了。
查看树的深度
分别计算左子树深度和右子树深度,更大的那个即为树的深度。
int TreeHeight(BTNode* root)
{
if (root == NULL)
return 0;
int leftHeight = TreeHeight(root->left) + 1;
int rightHeight = TreeHeight(root->right) + 1;
return (leftHeight > rightHeight ? leftHeight : rightHeight);
}
计算树第K层的结点个数
分治思想:第k层结点的个数等于第k-1层结点左右非空子树的个数,
第k-1层结点个数又等于第k-2层左右非空子树的个数,递推下去。
分而治之,递归处理。
int TreeKLever(BTNode* root, int k)
{
assert(k > 0);
if (root == NULL)
return 0;
if (k == 1)
return 1;
int leftK = TreeKLever(root->left, k - 1);
int rightK = TreeKLever(root->right, k - 1);
return leftK + rightK;
}
二叉树中查找值为x的结点
BTNode* BTFind(BTNode* root, BTDataType x)
{
if (root == NULL)
return NULL;
if (root->data == x)
return root;
BTNode* lret = BTFind(root->left, x);
if (lret)
return lret;
BTNode* rret = BTFind(root->right, x);
if (rret)
return rret;
return NULL;
}
层序遍历
层序遍历思路: 利用队列,每次都是先让根结点进队列,然后根节点出队列后将它的非空子结点入队列,以此类似迭代循环下去直至所有结点访问完毕。
//建议使用C++,用C语言的话,要把队列的头文件和源文件拷贝到当前文件夹中来调用
void LevelOrder(BTNode* root)
{
Queue q;
QueueInit(&q);
if (root)
QueuePush(&q, root);
while (!QueueEmpty(&q))
{
BTNode* front = QueueFront(&q);
QueuePop(&q);
printf("%d ", front->data);
if (front->left)
QueuePush(&q, front->left);
if (front->right)
QueuePush(&q, front->right);
}
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;
}