非线性结构之树
1. 树的定义和表示
树是n ( n≥0 ) 个结点的有限集合,当 n=0 时,成为空树;当 n≥0 时,该集合满足如下条件:
- 有且仅有一个根节点,该结点没有前驱,有 ≥0 个后继
- 除根节点之外的 n−1 个结点可划分为m( m≥0 )个互不相交的有限集 T1,T2,T3…,Tm ,每个 Ti 又是一棵树,称为根的子树,每颗子树的根结点有且仅有一个直接前驱,其前驱就是树的根节点,同时可以拥有零个或多个后继结点
2. 二叉树的定义
满足以下条件:
- 每个结点的度不大于2
- 结点每棵子树的位置是明确区分左右的,不能随意更改
相关的性质:
- 在二叉树的第
i
层上至多有
2i−1 个结点( i≥1 ) - 深度为k的二叉树至多有 2k−1 个结点
- 对任意一棵二叉树,若终端结点数为
n0
,度为2的结点的个数为
n2
,则满足
n0=n2+1
- 满二叉树:深度为
k
且含有
2k−1 个结点的二叉树 - 完全二叉树:深度为
k
,结点数为
n (n≤2k−1) 的二叉树,当且仅当其 n 个结点与满二叉树中连续编号为1 至 n 的位置的结点一一对应,称为完全二叉树
- 满二叉树:深度为
k
且含有
- 具有
n 个结点的完全二叉树的深度为 logn2+1 - 对于具有
n
个结点的完全二叉树,结点从0开始编号
- 如果
i=1 ,则结点 i 为根,其无双亲结点;如果i>1 ,则结点 i 的双亲结点序号为i/2 - 如果
2i≤n
,则结点
i
的左孩子结点序号为
2i ,否则,结点 i 无左孩子 - 如果
2i+1≤n ,则结点 i 的右孩子的结点的序号为2i+1 ,否则,结点 i <script type="math/tex" id="MathJax-Element-78">i</script>无右孩子
- 如果
3. 二叉树的创建
二叉树的链式存储结构:
typedef char elemType;
typedef struct BiNode {
elemType data;
struct BiNode *left, *right;
}BiNode, *BiTree;
二叉树的创建:
void createTree(BiTree *root) {
elemType data;
scanf("%c", &data);
if(data == '#') {
(*root) = NULL;
} else {
(*root) = (BiTree)malloc(sizeof(BiNode));
(*root)->data = data;
createTree(&((*root)->left));
createTree(&((*root)->right));
}
}
在写上面的代码时候,就出现了问题,原因就是一个自己的习惯吧,在开始的时候,我判断了一下(*root)的值是不是NULL,这个当main函数调用createTree函数的时候,这个判断是成立的,可以当递归调用的时候,就会出现问题。
4.二叉树的遍历
先序遍历二叉树:根结点,左子树,右子树
中序遍历二叉树:左子树,根节点,右子树
后序遍历二叉树:左子树,右子树,根节点
先序遍历二叉树的代码:
void preTravel(BiTree root) {
if(root != NULL) {
printf("%c ", root->data);
preTravel(root->left);
preTravel(root->right);
}
}
5. 统计结点的个数
void orderNodes(BiTree root) {
if(root) {
count++;
orderNodes(root->left);
orderNodes(root->right);
}
}
这里用到了一个全局变量count
6. 求二叉树的高度
void TreeDepth(BiTree root, int h) {
if(root) {
if(h > depth) {
depth = h;
}
TreeDepth(root->left, h+1);
TreeDepth(root->right, h+1);
}
}
7. 二叉树的层次遍历
ab#d##C#e##
void LevelOrder(BiTree root) {
SeQueue *Q = NULL;
BiTree p;
initQueue(&Q);
inQueue(Q, root);
while(!isEmpty(Q->rear, Q->front)) {
outQueue(Q, &p);
printf("%c ", p->data);
if(p->left != NULL) {
inQueue(Q, p->left);
}
if(p->right != NULL) {
inQueue(Q, p->right);
}
}
}
主要的思想就是:初始化一个队列,将根节点加入到队列中,然后根节点出队,然后访问根节点的左孩子,若不为空则将左孩子入队列,然后同理访问右孩子,只要这个队列不为空,就说明还没有遍历完这棵二叉树,则要接着进行出队和入队操作。
8. 由遍历序列确定二叉树
由先序和中序序列确定二叉树
先序遍历中的第一个结点就是根结点D
由根节点D分割中序序列,根结点之前是左子树L的中序序列,根节点之后是右子树R的中序序列
根据左子树的L的结点个数,分割先序序列,第一个是根节点,之后是左子树L的先序序列,最后是右子树R的先序序列
由中序和后序序列确定二叉树
由后序序列的最后一个结点确定根结点D
由根节点D分割中序序列,D之前是左子树L的中序序列,D之后是右子树R的中序序列,同时获得L和R的结点个数
根据左子树L的结点个数,分割后序列:首先是左子树L的后序序列,随后是右子树R的后续序列,最后是根