什么是树
树(Tree)是n(n≥0)个结点的有限集。n=0时称为空树。在任意一颗非空树中:(1)有且仅有一个特定的成为根(Root)d的结点;(2)当n>1时,其余结点可分为m(m>0)个互不相交的有限集T1、T2… …Tm,其中每一个集合本身又是一颗树,并且成为根的子树(SubTree)。
可以看到不同结点的孩子个数可以是0、1、2、3… …
什么是二叉树
二叉树也是n(n≥0)个结点的有限集。与树定义不同的是,该集合或者为空集(成为空二叉树),或者有一个根结点和互不相交的左子树和右子树组成。
而二叉树不同结点的孩子个数只能是0、1或2个。
二叉树的建立
对于一个结点,如果有左孩子,那就该结点左孩子域指向左孩子的地址,如果没有左孩子,也要赋值NULL,告诉计算机左孩子域为空,这里没有左孩子。右孩子同理。所以我们在构建二叉树时,需要将二叉树扩充为完全树。
二叉树的二叉链表结点结构定义
typedef int TElemType;
typedef struct BiTNode/*结点结构*/
{
TElemType data;
struct BiTNode* lchild, *rchild;
}BiTNode,*BiTree;
按前序输入二叉树中结点的值(一个字符)
//#表示空树,构造二叉链表表示二叉树T
void CreateBiTree(BiTree T)
{
TElemType ch;
scanf("%c", &ch);
if (ch == '#')
T = NULL;
else
{
T = (BiTree)malloc(sizeof(BiTree));
if (!T) //开辟动态空间是否成功
exit(OVERFLOW);//OVERFLOW, #include<math.h>
T->data = ch;
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
}
}
按中序输入二叉树中结点的值(一个字符)
//#表示空树,构造二叉链表表示二叉树T
void CreateBiTree(BiTree T)
{
CreateBiTree(T->lchild);
TElemType ch;
scanf("%c", &ch);
if (ch == '#')
T = NULL;
else
{
T = (BiTree)malloc(sizeof(BiTree));
if (!T) //开辟动态空间是否成功
exit(OVERFLOW);
T->data = ch;
CreateBiTree(T->rchild);
}
}
按后序输入二叉树中结点的值(一个字符)
//#表示空树,构造二叉链表表示二叉树T
void CreateBiTree(BiTree T)
{
CreateBiTree(T->lchild);
CreateBiTree(T->rchild);
TElemType ch;
scanf("%c", &ch);
if (ch == '#')
T = NULL;
else
{
T = (BiTree)malloc(sizeof(BiTree));
if (!T) //开辟动态空间是否成功
exit(OVERFLOW);
T->data = ch;
}
}
二叉树的遍历
前序遍历算法
//二叉树的前序遍历递归算法
void PreOrderTraverse(BiTree T)
{
if (T == NULL)//对指针是否为空进行判断
return;
printf("%c", T->data);/*显示结点数据,可以更改为其他对结点的操作*/
PreOrderTraverse(T->lchild);/*前序遍历左子树*/
PreOrderTraverse(T->rchild);/*前序遍历右子树*/
}
中序遍历算法
//二叉树的中序遍历递归算法
void InOrderTraverse(BiTree T)
{
if (T == NULL)
return;
InOrderTraverse(T->lchild);/*中序遍历左子树,遇到NULL停止*/
printf("%c", T->data);/*显示结点数据,可以更改为其他对结点的操作*/
InOrderTraverse(T->rchild);/*中序遍历左子树*/
}
后序遍历算法
/*二叉树的后序遍历算法*/
void InOrderTraverse(BiTree T)
{
if (T == NULL)
return;
InOrderTraverse(T->lchild);/*中序遍历左子树*/
InOrderTraverse(T->rchild);/*中序遍历左子树*/
printf("%c", T->data);/*显示结点数据,可以更改为其他对结点的操作*/
}
什么是线索二叉树
前面我们已经看到,二叉树在建立时需要大量的NULL指针,这样会造成时间和空间浪费。如何把这些空指针域利用起来呢?
所以有人提出,把空的 左孩子 域指向前驱,空的 右孩子 域指向后驱。
以中序遍历为例:
但是计算机如何知道里面存的是指向孩子的指针还是指向前驱或后驱的指针呢?
可以再增设两个标志域LTag、RTag,遍历过程中如果(左、右)孩子域为NULL,则赋值为1,否则为0。这样我们就可以通过标志域存的值间接判断孩子域存的是什么。
线索二叉树的建立(中序遍历线索化)
还是以中序遍历线索化为例
//线索二叉树
/*充分利用空指针*/
/*二叉树的二叉线索存储结构定义*/
typedef enum{Link,Thread}PointerTag;/*Link==0表示指向左右孩子指针*/
/*Thread==1表示指向前驱或后驱的线索*/
typedef struct BiThrNode /*二叉线索存储结点结构*/
{
TElemType data; /*结点数据*/
struct BiThrNode* lchild, * rchild; /*左右孩子指针*/
PointerTag LTag;
PointerTag RTag; /*左右标志*/
}BiThrNode,*BiThrTree;
/*中序遍历线索化的递归函数代码如下*/
BiThrTree pre;/*全局变量,始终指向刚刚访问过的结点*/
/*中序遍历进行中序线索化*/
void InThreading(BiThrTree T)
{
if (!T)
return;
InThreading(T->lchild);
T->LTag = Thread;
T->lchild = pre;
if (pre && !pre->rchild) /*排除pre开始等于NULL的情况*/
{
pre->RTag = Thread;
pre->lchild = T;
}
pre = T;
InThreading(T->rchild);
}