二叉树的创建(先序创建的)及先序遍历 中序遍历 后序遍历的递归和非递归实现

来源:互联网 发布:网络工程学院 编辑:程序博客网 时间:2024/05/16 07:22

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; // 处理其右子树            }        }    }}



0 0
原创粉丝点击