1. 首先是关于二树结点的设计
typedefstruct TreeNode *PtrToNode;
typedef PtrToNode BinTree;
struct TreeNode
{
char data;//结点数据,假设是字符
BinTree left; // 指向左子树
BinTree right; // 指向右子树
};
2. 其次是二叉树创建的模拟
根据先序次序输入二叉树的结点,从而创建一个二叉树(‘-’字符表示树空,当然也可以是其他字符,比如空格);
例如一棵树结构如下,其输入次序为:abd--fe---cg-h--i--
二叉树根据键盘输入字符的创建模拟函数
BinTree binTreeCreate()
{
BinTree t;
char temp;
scanf("%c", &temp);
if ('-' == temp)
t = NULL;
else
{
t = (BinTree)malloc(sizeof(struct TreeNode));
t->data = temp;
t->left = binTreeCreate(t->left);
t->right = binTreeCreate(t->right);
}
return t;
}
3. 递归实现的三种遍历(先序 中序 后序)
3.1 先序遍历二叉树的操作定义为:
若二叉树为空,则空操作;否则:
(1) 访问根结点
(2) 先序遍历左子树
(3) 先序遍历右子树
void binTreePreOrderTraversal(BinTree T)
{
if(T)
{
printf("%c ", T->data);
binTreePreOrderTraversal(T->left);
binTreePreOrderTraversal(T->right);
}
}
3.2 中序遍历二叉树的操作定义为:
若二叉树为空,则空操作;否则:
(1) 中序遍历左子树
(2) 访问根结点
(3) 中序遍历右子树
void binTreeInOrderTraversal(BinTree T)
{
if(T)
{
binTreeInOrderTraversal(T->left);
printf("%c ", T->data);
binTreeInOrderTraversal(T->right);
}
}
3.3 后序遍历二叉树的操作定义为:
若二叉树为空,则空操作;否则:
(1) 中序遍历左子树
(2) 中序遍历右子树
(3) 访问根结点
void binTreePostOrderTraversal(BinTree T)
{
if(T)
{
binTreePostOrderTraversal(T->left);
binTreePostOrderTraversal(T->right);
printf("%c ", T->data);
}
}
4. 非递归实现的三种遍历(先序 中序 后序)
思路是这样的:
先序、中序和后序遍历过程: 遍历过程中经过结点的路线一样,只是访问各结点的时机不同。
下图在从入口到出口的曲线上用三种符号分别标记出了先序、中序和后序访问各结点的时刻。
二叉树的非递归遍历算法实现的基本思路:使用堆栈,依据各结点的访问时序实现即可
4.1 先序遍历非递归思路:
遇到一个结点(第一次遇到),访问它,再把它压栈,并去遍历它的左子树;
当左子树遍历结束后,从栈顶弹出这个结点;
然后按其右指针再去先序遍历该结点的右子树。
void binTreePreOrderTraversal(BinTree T)
{
//创建并初始化堆栈S
Stack s = CreateStack(MAXSIZE);
while (T || !IsEmpty(s)) // 循环结点的条件是T空且栈空
{
while (T)
{
printf("%c ", T->data);
Push(s, T);
T = T->left;
}
if (!IsEmpty(s))
{
T = Pop(s);
T = T->right;
}
}
}
4.2 中序遍历非递归思路:
遇到一个结点,就把它压栈,并去遍历它的左子树;
当左子树遍历结束后,从栈顶弹出这个结点(第二次遇到该结点)并访问它;
然后按其右指针再去中序遍历该结点的右子树。
void binTreeInOrderTraversal(BinTree T)
{
// 创建并初始化堆栈S
Stack s = CreateStack(MAXSIZE);
while (T || !IsEmpty(s))
{
while (T)
{
Push(s, T);
T = T->left;
}
if (!IsEmpty(s))
{
T = Pop(s);
printf("%c ", T->data);
T = T->right;
}
}
}
4.3 后序遍历非递归思路:
遇到一个结点,就把它压栈(第一次遇到该结点),并去遍历它的左子树;
当左子树遍历结束后,从栈顶取出这个结点的值 (第二次遇到该结点),并不进行弹栈处理;
然后按其右指针再去后序遍历该结点的右子树;
当其右子树遍历结点后,从栈顶取出该值访问并弹栈;
由时序图可以看出每个结点都可以看作第三次才访问的,即其左右子树都已经进行过遍历,而无论其左右子树有无;
那么在树结点中增加一个flag字段记录结点访问的时序(第几次遇到该结点),很明显后序是第三次,那么可以这么设计:
首先树中每个结点的flag字段默认值为0,将结点首次压栈时,将其flag字段值+1(表明即将遍历其左子树);
依次遍历左子树,当左子树遍历结束后,退出小循环,对栈顶元素进行处理;
若栈顶元素的flag字段值为1,表明其左子树已经遍历完,然后按其右指针再去后序遍历该结点的右子树,flag字段值再+1(表明即将遍历其右子树),此时栈顶元素,并不出栈;
若栈顶元素的flag字段值为2,表明其右子树已经遍历完,那么访问该结点并将其出栈,再对栈中其它元素进行处理。
这里我偷懒就在原结构体上进行了添加字段
struct TreeNode
{
char data;//结点数据
BinTree left; // 指向左子树
BinTree right; // 指向右子树
int flag; //访问标志,方便后序非递归遍历
};
代码如下:
void binTreePostOrderTraversal(BinTree T)
{
//创建并初始化堆栈S
Stack s = CreateStack(MAXSIZE);
while (T || !IsEmpty(s))
{
while (T && T->flag==0)
{
T->flag++;//该结点第一次访问,将其入栈
Push(s, T);
T = T->left;
}
if (!IsEmpty(s))
{
T = Top(s);
if (T->flag ==2)//后序遍历,每个结点基本上可以看做第三次遇到才访问
{
T = Pop(s);
printf("%c ", T->data);
T = Top(s);
}
else
{
T->flag++;//该结点第二次遇到,此时结点在栈中,但不出栈
T = T->right; // 处理其右子树
}
}
}
}