数据结构之二叉树的遍历

来源:互联网 发布:苹果电脑能编程吗 编辑:程序博客网 时间:2024/06/11 23:48

前言:在上一篇中介绍了二叉树的基础知识,这一篇介绍遍历二叉树的实现

一、二叉树的存储结构:

        二叉树的存储结构可以采用顺序存储,也可以采用链式存储,其中链式存储更加灵活。

        在链式存储结构中,与线性链表类似,二叉树的每个结点采用结构体表示,结构体包含三个域:数据域、左指针、右指针。

二、二叉树的遍历:

二叉树实现需要用到的数据结构代码如下:

struct BiTNode{     //定义二叉树char data;     //每个结点的数据BiTNode *lchild, *rchild;     //左右孩子指针};struct Stack{     //定义栈int top;       //栈顶指针BiTNode *stacksize[100];      //栈的容量};void InitStack(Stack &S)    //初始化一个空栈{S.top = -1;}int Push(Stack &S, BiTNode *pt)     //元素进栈{S.stacksize[++S.top] = pt;return 1;}BiTNode * Pop(Stack &S)   //元素出栈{BiTNode *pt;pt = S.stacksize[S.top--];return pt;}BiTNode * GetTop(Stack S)      //获取栈顶元素{BiTNode *pt;if (S.top == -1)return 0;else{pt = S.stacksize[S.top];return pt;}}int IsEmptyStack(Stack S)   //判断是否空栈,空栈返回1,否则返回0{return S.top == -1;}


 二叉树遍历通常借用“栈”这种数据结构实现,有两种方式:递归方式及非递归方式。

(1)在递归方式中,栈是由操作系统维护的,用户不必关心栈的细节操作,用户只需关心“访问顺序”即可。因而,采用递归方式实现二叉树的遍历比较容易理解,算法简单,容易实现。

(2)借用“栈”采用非递归方式,也能实现遍历。但是,这时的栈操作(push、pop等)是由用户进行的,因而实现起来会复杂一些,而且也不容易理解,但有助于我们对树结构的遍历有一个深刻、清晰的理解。

(a)非递归先序遍历:

在遍历某一个二叉(子)树时,以一当前指针记录当前要处理的二叉(左子)树,以一个栈保存当前树之后处理的右子树。首先访问当前树的根结点数据,接下来应该依次遍历其左子树和右子树,然而程序的控制流只能处理其一,所以考虑将右子树的根保存在栈里面,当前指针则指向需先处理的左子树,为下次循环做准备;若当前指针指向的树为空,说明当前树为空树,不需要做任何处理,直接弹出栈顶的子树,为下次循环做准备,代码如下

int PreOrderTraverse(BiTNode *T)       //二叉树先序遍历的非递归算法{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;while (pt != NULL || IsEmptyStack(S)!=1){if (pt != NULL){cout << pt->data;Push(S, pt->rchild);  //右子树进栈,让当前指针指向左子树,为下一个循环做准备pt = pt->lchild;}elsept=Pop(S);  //左子树为空,不做任何处理,直接弹出右子树,为下一个循环准备}return 1;    //遍历完成}

相对于非递归先序遍历,非递归的中序/后序遍历稍复杂一点。

(b)非递归中序遍历:

若当前树不为空树,则访问其根结点之前应先访问其左子树,因而先将当前根节点入栈,然后考虑其左子树,不断将非空的根节点入栈,直到左子树为一空树;当左子树为空时,不需要做任何处理,弹出并访问栈顶结点,然后指向其右子树,为下次循环做准备。代码如下:

int InOrderTraverse(BiTNode *T)   //二叉树中序非递归遍历{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;while (pt != NULL || IsEmptyStack(S)!=1){if (pt != NULL){Push(S, pt);    //访问根节点之前先访问左结点,所以现将根节点进栈pt = pt->lchild;}else        //根指针退栈,访问根节点,遍历右子树{pt=Pop(S);cout << pt->data;pt = pt->rchild;     //访问根节点之后,访问右子树}}return 1;}

(c)后序遍历的非递归实现是三种遍历方式中最难的一种。因为在后序遍历中,要保证左孩子和右孩子都已被访问,并且左孩子在右孩子之前访问才能访问根结f点,这就为流程控制带来了难题。下面介绍一种思路。

     要保证根结点在左孩子和右孩子访问之后才能访问,因此对于任一结点p,先将其入栈。若p不存在左孩子和右孩子,则可以直接访问它,或者p存在左孩子或右孩子,但是其左孩子和右孩子都已经被访问过了,则同样可以直接访问该结点。若非上述两种情况,则将p的右孩子和左孩子依次入栈,这样就保证了每次取栈顶元素的时候,左孩子在右孩子之前别访问,左孩子和右孩子都在根结点前面被访问。代码如下:

int PostOrderTraverse(BiTNode *T)       //二叉树的后序非递归算法{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;BiTNode *pre, *cur;pre = NULL;if (!pt)             //空树{cout << "the tree is null" << endl;return 0;}Push(S, pt);while (IsEmptyStack(S)!=1){cur =GetTop(S);  //获取栈顶元素if ((cur->lchild == NULL&&cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild))){//如果当前结点没有孩子或者左右孩子都已经访问过,就直接访问该结点cout << cur->data;Pop(S);pre = cur;       //标识它成为上一个被访问的元素}else{if (cur->rchild != NULL)      //当前结点既有左右孩子没有被访问,那么就该结点就进栈Push(S, cur->rchild);if (cur->lchild != NULL)Push(S, cur->lchild);}}return 1;}


三、完整程序代码:

#include "stdafx.h"#include <iostream>using namespace std;struct BiTNode{     //定义二叉树char data;     //每个结点的数据BiTNode *lchild, *rchild;     //左右孩子指针};struct Stack{     //定义栈int top;       //栈顶指针BiTNode *stacksize[100];      //栈的容量};void InitStack(Stack &S)    //初始化一个空栈{S.top = -1;}int Push(Stack &S, BiTNode *pt)     //元素进栈{S.stacksize[++S.top] = pt;return 1;}BiTNode * Pop(Stack &S)   //元素出栈{BiTNode *pt;pt = S.stacksize[S.top--];return pt;}BiTNode * GetTop(Stack S)      //获取栈顶元素{BiTNode *pt;if (S.top == -1)return 0;else{pt = S.stacksize[S.top];return pt;}}int IsEmptyStack(Stack S)   //判断是否空栈,空栈返回1,否则返回0{return S.top == -1;}int PreOrderTraverse(BiTNode *T)       //二叉树先序遍历的非递归算法{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;while (pt != NULL || IsEmptyStack(S)!=1){if (pt != NULL){cout << pt->data;Push(S, pt->rchild);  //右子树进栈,让当前指针指向左子树,为下一个循环做准备pt = pt->lchild;}elsept=Pop(S);  //左子树为空,不做任何处理,直接弹出右子树,为下一个循环准备}return 1;    //遍历完成}int InOrderTraverse(BiTNode *T)   //二叉树中序非递归遍历{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;while (pt != NULL || IsEmptyStack(S)!=1){if (pt != NULL){Push(S, pt);    //访问根节点之前先访问左结点,所以现将根节点进栈pt = pt->lchild;}else        //根指针退栈,访问根节点,遍历右子树{pt=Pop(S);cout << pt->data;pt = pt->rchild;     //访问根节点之后,访问右子树}}return 1;}int PostOrderTraverse(BiTNode *T)       //二叉树的非递归算法{Stack S;InitStack(S);BiTNode *pt = NULL;pt = T;BiTNode *pre, *cur;pre = NULL;if (!pt){cout << "the tree is null" << endl;return 0;}Push(S, pt);while (IsEmptyStack(S)!=1){cur =GetTop(S);  //获取栈顶元素if ((cur->lchild == NULL&&cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild))){//如果当前结点没有孩子或者左右孩子都已经访问过,就直接访问该结点cout << cur->data;Pop(S);pre = cur;       //标识它成为上一个被访问的元素}else{if (cur->rchild != NULL)      //当前结点既有左右孩子没有被访问,那么就该结点就进栈Push(S, cur->rchild);if (cur->lchild != NULL)Push(S, cur->lchild);}}return 1;}int CreateBiTree(BiTNode *&T)   //采用先序遍历法创建二叉树{char c;c = getchar();if (c == '#')    //  空树T = NULL;else{T = new BiTNode;T->data = c;CreateBiTree(T->lchild);CreateBiTree(T->rchild);}return 1;}int _tmain(int argc, _TCHAR* argv[])   {BiTNode *T = NULL;cout << "先序输入字符创建一个二叉树:" << endl;CreateBiTree(T);cout << "前序遍历二叉树序列为:" << endl;PreOrderTraverse(T);cout << endl;cout << "中序遍历二叉树序列为:" << endl;InOrderTraverse(T);cout << endl;cout << "后序遍历二叉树序列为:" << endl;PostOrderTraverse(T);cout << endl;return 0;}

参考:严蔚敏《数据结构》(c语言版)

0 0