本系列是记录与总结性质的文章,原创的内容少,记录的内容大都与考研有关。在考研的范畴里,与树相关的算法很多,程序设计题中属于必考题。我准备用三篇博客来总结与树有关的算法。
因为与考研相关,数据结构的定义和规范参考严书1 和王道2。前一本是计算机考研的指定参考书,后一本是考研辅导书。
最后,由于写博客的时间仓促,文中若有错误之处,恳请朋友们批评指正。
在考试中,实现树的相关算法时很可能会用到栈或队列,可以直接把他们作为一个工具来解决问题。即,把栈或队列的声明和操作写得很简单,不必分函数写出。以顺序栈的操作为例:
(1) 声明一个栈并初始化:
//下面两句连声明带初始化都有了
ElemType stack[maxSize];
int top = -1;
(2)元素进栈
stack[++top] = x; //仅一句话即实现进栈操作
(3)元素x出栈
x = stack[top--];
更简单的,可以直接使用栈和队列的基本操作的函数来实现操作。栈的基本操作有:
InitStack(S); //初始化
StackEmpty(S); //判空
Push(S,x); //元素x进栈
Pop(S,x); //栈顶元素出栈并赋值给x
GetTop(S,x); //读栈顶元素并赋值给x
相应的,队列的基本操作如下:
InitQueue(Q); //初始化队列
QueueEmpty(Q); //判空
EnQueue(Q,x); //元素x入队
DeQueue(Q,x); //出队,赋值给x
GetHead(Q,x); //读队头元素,赋值给x
typedef struct BiTNode{
ElemType data;
struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
二叉树的遍历,递归算法写起来非常简单。重点在与如何用非递归算法来实现遍历。
1.先序遍历(PreOrder)
void PreOrder(BiTree T){
if(T != NULL){
visit(T);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
2.中序遍历(InOrder)
void InOrder(BiTree T){
if(T != NULL){
InOrder(T->lchild);
visit(T);
InOrder(T->rchild);
}
}
3.后序遍历(PostOrder)
void PostOrder(BiTree T){
if(T != NULL){
PostOrder(T->lchild);
PostOrder(T->rchild);
visit(T);
}
}
1.先序遍历
void PreOrder2(BiTree T){
BiTree p = T;
InitStack(S); //初始化一个空栈
if(p != NULL){
Push(S,p); //结点进栈
while(!StackEmpty(S)){
Pop(S,p); //出栈,赋值给p
visit(p);
if(p->rchild){ //一定是右孩子先入栈
Push(S,p-rchild);
}
if(p->lchild){
Push(S,p->lchild);
}
}
}
}
2.中序遍历
void InOrder2(BiTree T){
BiTree = T;
InitStack(S);
while(p || !StackEmpty(S)){
if (p != NULL){
Push(S,p);
p = p->lchild;
}else{
Pop(S,p);
visit(p);
p = p->rchild;
}
}
}
3.后序遍历(重点难点)
void PostOrder2(BiTree T){
BiTree p;
InitStack(S);
while(T || !StackEmpty(S)){
while(T){
Push(S,T);
T = T->lchild;
}
flag = 1; //flag = 1代表左孩子为空或者该节点已经被访问
p = NULL; //p指向该节点的前驱
while(!StackEmpty(S) && flag == 1){
GetTop(S,T); //获取栈顶元素,非出栈
if(T->rchild == p){
Pop(S,T);
visit(T);
p = T;
}else{
T = T->rchild;
flag = 0;
}
}
}
}
注: 上面的后续遍历没有直接copy王道数据结构P175页的代码,但二者思路是一致的。
之所以说后序遍历的非递归算法是重点难点,是因为每当访问一个结点x时,此时栈里存储的元素正好是x的所有祖先。后面的题目里求叶子结点的所有祖先,或者输出根到叶子的路径,都和这个非递归的后序遍历紧密相关。
4.层次遍历
void LevelOrder(BiTree T){
BiTree p;
InitQueue(Q);
EnQueue(Q,T); //根节点入队
while(!QueueEmpty(Q)){
DeQueue(Q,p); //出队,赋值给p
visit(p);
if(p->lchild){
EnQueue(Q,p->lchild);
}
if(p->rchild){
EnQueue(Q,p->rchild);
}
}
}