一、结点定义
typedef int ElemType;
typedef struct BiTNode {
ElemType data; //数据域
struct BiTNode *lchild; //左孩子
struct BiTNode *rchild; //右孩子
} BiTNode, *BiTree;
二、树的遍历
1、递归写法
先序遍历:先访问根结点,再先序遍历左子树,再先序遍历右子树。
void PreOrderTraverse(BiTree T) { //先序遍历
if (T != NULL) {
visit(T); //访问根结点
PreOrderTraverse(T->lchild);//访问左子树
PreOrderTraverse(T->rchild);//访问右子树
}
}
中序遍历:先中序遍历左子树,再访问根结点,再中序遍历右子树。
void InOrderTraverse(BiTree T) { //中序遍历
if (T != NULL) {
InOrderTraverse(T->lchild);//访问左子树
visit(T); //访问根结点
InOrderTraverse(T->rchild);//访问右子树
}
}
后序遍历:先后序遍历左子树,再后续遍历右子树,最后访问根结点。
void PostOrderTraverse(BiTree T) { //后序遍历
if (T != NULL) {
PostOrderTraverse(T->lchild);//访问左子树
PostOrderTraverse(T->rchild);//访问右子树
visit(T); //访问根结点
}
}
2、非递归写法
先序遍历:利用栈,令P等于根结点,作为工作结点,如果P非空或栈非空,则重复以下操作:如P非空,则访问P,然后将P入栈,再令P等于其左孩子;若P指向空,说明P已经到达最左边的结点的左孩子,且左孩子为空,则将栈顶元素出栈到P,此时P的左子树已访问完毕,接下来访问右子树,令P等于其右孩子,重复以上操作。
void PreOrderTraverse1(BiTree T) { //非递归先序遍历
if (T == NULL) {
return;
}
LinkStack S;
initStack(S);//初始化链栈
BiTree P = T;//P为工作结点
while (P != NULL || !isEmpty(S)) { //P不空且栈非空
if (P != NULL) {
visit(P);//访问当前结点
push(S, P);//当前结点入栈,因为右子树还未访问
P = P->lchild;
} else {
pop(S, P);//左子树已为空,出栈
P = P->rchild;//访问右子树
}
}
}
中序遍历:和先序遍历类似,只需将访问P的操作放到出栈后即可。即先一路将根结点及左下结点依次入栈,当达到最左下结点时,出栈并访问它,然后转向右子树。
void InOrderTraverse1(BiTree T) { //中序遍历
if (T == NULL) {
return;
}
LinkStack S;
initStack(S);
BiTree P = T;
while (P != NULL || !isEmpty(S)) {
if (P != NULL) {
push(S, P);
P = P->lchild;
} else {
pop(S, P);
visit(P);
P = P->rchild;
}
}
}
后序遍历:类比中序遍历,1、先沿着根节点,依次将左孩子入栈,直到左孩子为空。2、此时取栈顶元素P,由于要想访问当前结点P,则必须满足要么P的右孩子已经被访问,要么P的右孩子为空,因此可设置一个辅助指针r,用来指向最近一次访问过的结点。如果P满足上述条件,则可访问P,并将栈顶元素出栈,由于P此时已被访问,所以已P为根的子树均已被访问,且表示P的父节点的左子树已访问,所以接着向上回溯,对其父结点执行2。如果访问到P,其右孩子不为空,则令P等于其右孩子,继续执行1。
void PostOrderTraverse1(BiTree T) { //后序遍历
if (T == NULL) {
return;
}
LinkStack S;
initStack(S);
BiTree P = T;
BiTNode *r = NULL;//记录最近访问过的结点
while (P != NULL || !isEmpty(S)) {
if (P != NULL) {
push(S, P);//左孩子入栈
P = P->lchild;
} else {
GetTop(S, P);//左孩子为空,取栈顶元素
if (P->rchild == r || P->rchild == NULL) {//右孩子已被访问过或没有右孩子
pop(S, P);//出栈
visit(P);//访问
r = P;//更新r
P = NULL;//P置空,以便取下一个栈顶元素
} else {
P = P->rchild;//右孩子存在且未被访问,继续循环
}
}
}
}
3、层次遍历
借助一个队列,先将根节点入队,若队列非空,则进行以下操作:队头元素出队,并访问,如其还有左孩子,则左孩子入队,若有右孩子,右孩子入队。反复执行,直到队列为空。
void LevelOrderTraverse(BiTree T) { //层次遍历
LinkQueue Q;
InitQueue(Q); //初始化一个链队
BiTree P;
EnQueue(Q, T); //根结点入队
while (!isEmpty(Q)) { //队列非空
DeQueue(Q, P); //队头元素出队
visit(P); //访问队头元素
if (P->lchild != NULL) //左孩子非空
EnQueue(Q, P->lchild); //左孩子入队
if (P->rchild != NULL)//右孩子非空
EnQueue(Q, P->rchild);//右孩子入队
}
}