1 - 次序遍历
1.1 先序遍历
递归算法
若二叉树为空,则什么也不做;否则,
- 访问根节点;
- 先序遍历左子树;
- 先序遍历右子树。
void PreOrder(BiTree T)
{
if (T != NULL)
{
visit(T); //访问根节点
PreOrder(T->lchild); //递归遍历左子树
PreOrder(T->rchild); //递归遍历右子树
}
}
时间复杂度: ,每个节点都访问且仅访问一次;
空间复杂度: ,递归遍历中递归工作栈的栈深为树的深度。
非递归算法
- 沿着根的左孩子,依次访问并入栈,直到左孩子为空;
- 栈顶元素出栈:
- 若其右孩子为空,继续执行 2 ;
- 若其右孩子非空,将右子树转执行 1 。
void PreOrder2(BiTree T)
{
InitStack(S); BiTree p = T; //初始化栈S;p是遍历指针
while (p || !IsEmpty(S)) //栈不空或p不空时循环
{
if (p) //一路向左
{
visit(p); Push(S, p); //访问当前结点,并入栈
p = p->lchild; //左孩子不空,一直向左走
}
else //出栈,并转向出栈结点的右子树
{
Pop(S, p); //栈顶元素出栈
p = p->rchild; //向右子树走,p赋值为当前结点的右孩子
} //返回while循环继续进入if-else语句
}
}
1.2 中序遍历
递归算法
若二叉树为空,则什么也不做;否则,
- 中序遍历左子树;
- 访问根节点;
- 中序遍历右子树。
void InOrder(BiTree T)
{
if (T != NULL)
{
InOrder(T->lchild); //递归遍历左子树
visit(T); //访问根节点
InOrder(T->rchild); //递归遍历右子树
}
}
时间复杂度: ,每个节点都访问且仅访问一次;
空间复杂度: ,递归遍历中递归工作栈的栈深为树的深度。
非递归算法
- 沿着根的左孩子,依次入栈,直到左孩子为空,说明已经找到可以输出的结点;
- 栈顶元素出栈并访问:
- 若其右孩子为空,继续执行 2 ;
- 若其右孩子非空,将右子树转执行 1 。
void InOrder2(BiTree T)
{
InitStack(S); BiTree p = T; //初始化栈S;p是遍历指针
while (p || !IsEmpty(S)) //栈不空或p不空时循环
{
if (p) //一路向左
{
Push(S, p); //当前结点入栈
p = p->lchild; //左孩子不空,一直向左走
}
else //出栈,并转向出栈结点的右子树
{
Pop(S, p); visit(p); //栈顶元素出栈,访问出栈结点
p = p->rchild; //向右子树走,p赋值为当前结点的右孩子
} //返回while循环继续进入if-else语句
}
}
1.3 后序遍历
递归算法
若二叉树为空,则什么也不做;否则,
- 后序遍历左子树;
- 后序遍历右子树。
- 访问根节点;
void PostOrder(BiTree T)
{
if (T != NULL)
{
PostOrder(T->lchild); //递归遍历左子树
PostOrder(T->rchild); //递归遍历右子树
visit(T); //访问根节点
}
}
时间复杂度: ,每个节点都访问且仅访问一次;
空间复杂度: ,递归遍历中递归工作栈的栈深为树的深度。
非递归算法
- 沿着根的左孩子,依次入栈,直到左孩子为空
- 读栈顶元素:
- 若其右孩子不空且未被访问过,将右子树转执行 1 ;
- 否则,栈顶元素出栈并访问
上述思想的第 2 步中,必须分清返回时是从左子树返回的还是从右子树返回,因此设定一个辅助指针
r,指向最近访问过的节点。(也可以在节点中增加一个标志域,记录是否已被访问)
void PostOrder2(BiTree T)
{
InitStack(S);
p = T;
r = NULL;
while (p || !IsEmpty(S))
{
if (p) //走到最左边
{
Push(S, p);
p = p->lchild;
}
else //向右
{
GetTop(S, p); //读栈顶结点(非出栈)
if (p->rchild && p->rchild != r) //若右子树存在,且未被访问过
p = p->rchild; //转向右
else //否则,弹出节点并访问
{
Pop(S, p); //将节点弹出
visit(p->data); //访问该节点
r = p; //记录最近访问过的节点
p = NULL; //节点访问完后,重置p指针
}
}
}
}
注意:每次出栈访问完一个根节点就相当于遍历完以该根节点为根的子树,需将
p置为NULL
2 - 层序遍历
先将二叉树根节点入队,然后出队,访问出队节点,若它有左子树,则将左子树根节点入队;若它有右子树。则将右子树根节点入队。然后出队,访问出队节点……如此反复,直至队列为空。
void LevelOrder(BiTree T)
{
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); //右子树不空,则右子树根节点入队
}
}