全球彩票平台_全球彩票注册平台|官网下载地址

热门关键词: 全球彩票平台,全球彩票注册平台,全球彩官网下载地址

【全球彩票注册平台】二叉树非递归遍历,线索

关于二叉树的遍历梳理(递归、非递归、线索二叉树),递归二叉树

  二叉树作为的基本数据结构,应用广泛,在生活中处处可见,而遍历二叉树在二叉树应用中十分常见。与线性存储结构不同,二叉树每个节点都有可能有两棵子树,从二叉树的存储结构可知:

1 template <typename T>
2 typedef struct BiTNode
3 {
4     T data;
5     struct BiTNode *lchild, *rchild;
6 }BiTree;

  根节点、左子树、右子树——二叉树的基本组成单位。那么,根据的递归的思想(数据结构严蔚敏版):当一个复杂的问题可以分解成若干子问题来处理时,其中某些子问题与原问题有相同的特征属性,则可利用和原问题相同的分析处理方法;反之,这些子问题解决了,原问题也就迎刃而解了。递归遍历就是将原二叉树分解为若干子树。

 1 template <typename T>
 2 int PreOrderTraverse(BiTree & Tree, int (*Visit)(T & e))
 3 {
 4     //Visit函数理解为打印e,若成功返回1,否则返回0。
 5     if (Tree) //判断树是否为空树
 6     {
 7         if (Visit(Tree.data)) //打印节点数据
 8             if (PreOrderTraverse(Tree.lchild, Visit) //向左遍历
 9                 if (PreOrderTraverse(Tree.rchild, Visit) return 1; //左遍历退出遍历右侧
10         return 0;
11     }
12     else return 1;
13 }

  从递归工作过程可以看出,第i层栈顶,左子树返回i-1层进行右子树遍历,而右子树返回则直接删除栈顶返回(这取决于遍历的方式:先序遍历,中序遍历,后序遍历)。

  由此,可以模仿递归算法写出非递归算法的遍历函数。

  

 1 template <typename T>
 2 void PreOrderTraverse(BiTNode & Tree, int (*Visit(T e))
 3 {
 4     InitStack(S); //初始化栈S
 5     BiTree p; 
 6     Push(S, Tree); //根节点入栈
 7     while (!StackEmpty(S))
 8     {
 9         while (GetTop(S, p) && p) 
10         {
11             Visit(p.data);
12             Push(S,(p.lchild); //左子树压栈
13         } //while
14         Pop(S, *p); //空指针出栈,用p返回其节点---------1
15         if (!StackEmpty(S))
16         {
17             Pop(S, p); //-------------------------------2
18             Push(S,p.rchild);
19         } //if
20     } //while
21 }

  使用栈来管理遍历的过程,由于是先序遍历,所以每向左压一次栈时便Visit一次节点,到达最左端时(依次从根节点Visit了左子树各子树的根节点)向右压栈,到达右叶子节点时回退,由于第17行的退栈,会使得栈顶变为上上层的根节点。也就是遍历右子树时不需要保存当前层的根指针,可直接修改栈顶的指针。说的有些不清楚,但这和递归算法思路一致,不过是自己管理栈的进出。

  注意,粗略一看可能会产生疑惑:Pop退栈p多退了一层。这里第一个Pop函数退出空指针,p是其返回值也为空,经过第二个Pop函数p才为有效栈顶。就比如,小红组装某个物件将步骤列了个清单,她将要着手去做这一步骤时,将该步骤从清单划去,划去的这一时刻是上一件事产生的影响。

  这两种遍历是对非线性结构进行线性化操作,引用双向链表的前驱后继的思想,修改二叉树的现有数据结构,新增两个标志域——LTag和RTag。

 1 template <typename T>
 2 typedef struct BiThrNide
 3 {
 4     T data;
 5     int LTag,RTag;
 6     BiThrTree *lchild, *rchild;
 7 }BiThrTree;
 8 
 9 void InOrderTraverse(BiThrTree & Tree, int (*Visit(T e)))
10 {
11     //Tree的lchild指向树的根节点
12     //标志符LTag、RTag为0则指针域指向孩子节点,为1指向前驱或后继
13     BiThrTree p;
14     while (p != Tree)
15     {
16         while (p.LTag == 0) p = p.lchild; //向左遍历
17         if (!Visit(p.data)) exit(ERROR);
18         while (p.RTag == 1 && p.rchild != Tree) //没有右子树,指向后继,且后继不为头结点
19         {
20             P = P.rchild; //向后继遍历
21             Visit(p.data);
22         }
23         p = p.rchild;
24     }
25 }

  由于线索二叉树存储结构较普通二叉树丰满,所以遍历过程也更为好理解,此处不做赘述。

  

二叉树作为的基本数据结构,应用广泛,在生活中处处可见,而遍历二...

  二叉树作为的基本数据结构,应用广泛,在生活中处处可见,而遍历二叉树在二叉树应用中十分常见。与线性存储结构不同,二叉树每个节点都有可能有两棵子树,从二叉树的存储结构可知:

来源:

  1. 二叉树遍历定义
    遍历二叉树,即遵从某种次序访问二叉树中的所有节点,使得每个节点仅被访问一次。这里提到的“访问”是指对节点施行某种操作,可以是输出节点信息,修改节点值等,但要求这种访问不破坏它原来的数据结构。在本文中,遍历操作操作为访问并输出节点值,且以二叉链表作为二叉树的存贮结构。由于二叉树是一种非线性结构,每个节点可能有一个以上的直接后继,因此,必须规定遍历的规则,并按此规则遍历二叉树,最后得到二叉树所有节点的一个线性序列。

  对于一种数据结构而言,遍历是常见操作。二叉树是一种基本的数据结构,是一种每个节点的儿子数目都不多于2的树。二叉树的节点声明如下:

1 template <typename T>2 typedef struct BiTNode3 {4     T data;5     struct BiTNode *lchild, *rchild;6 }BiTree;

【bitree.cpp】

令L,R,D分别代表二叉树的左子树、右子树、根节点,则遍历二叉树有6种规则:DLR、DRL、LDR、LRD、RDL、RKD。若规定二叉树中必须先左后右(左右顺序不能颠倒),则只有DLR、LDR、LRD三种遍历规则。DLR称为前根遍历(或前序遍历、先序遍历、先根遍历),LDR称为中根遍历(或中序遍历),LRD称为后根遍历(或后序遍历)。

1 typedef struct TreeNode *PtrToNode;
2 typedef struct TreeNode *BinTree;
3 
4 struct TreeNode
5 {
6     int Data;    //为简单起见,不妨假设树节点的元素为int型
7     BinTree Left;
8     BinTree Right;
9 };

  根节点、左子树、右子树——二叉树的基本组成单位。那么,根据的递归的思想:当一个复杂的问题可以分解成若干子问题来处理时,其中某些子问题与原问题有相同的特征属性,则可利用和原问题相同的分析处理方法;反之,这些子问题解决了,原问题也就迎刃而解了。递归遍历就是将原二叉树分解为若干子树。

 

二叉树结构定义为:
typedef struct _BinaryTreeNode
{
int value;
struct _BinaryTreeNode* pLeft; //指向左子节点的指针
struct _BinaryTreeNode* pRight; //指向右子节点的指针
}BinaryTreeNode_t;

  二叉树的遍历主要有先序遍历,中序遍历,后序遍历,层序遍历四种方式,下面一一介绍。

 1 template <typename T> 2 int PreOrderTraverse(BiTree & Tree, int (T & e)) 3 { 4     //Visit函数理解为打印e,若成功返回1,否则返回0。 5     if  //判断树是否为空树 6     { 7         if (Visit(Tree.data)) //打印节点数据 8             if (PreOrderTraverse(Tree.lchild, Visit) //向左遍历 9                 if (PreOrderTraverse(Tree.rchild, Visit) return 1; //左遍历退出遍历右侧10         return 0;11     }12     else return 1;13 }

全球彩票注册平台 1

2.先根遍历
所谓先根遍历,就是根节点最先遍历,其次左子树,最后右子树。

  1. 先序遍历

  从递归工作过程可以看出,第i层栈顶,左子树返回i-1层进行右子树遍历,而右子树返回则直接删除栈顶返回(这取决于遍历的方式:先序遍历,中序遍历,后序遍历)。

/*******************************************************************************
/* <PRE>
/* 版权所有    : -
/* 模块名      : 树
/* 文件名      : bitree.cpp
/* 功能描述    : 二叉树的非递归遍历
/* 作者        : <xxx>
/* 版本        : 1.0
/* -----------------------------------------------------------------------------
/* 备注        : 输入示例与输出结果
/* 
/* e.g. input  : ABD###CE#F###
/* bi-tree     :
/*                 A
/*                / 
/*               B   C
/*              /   /
/*             D   E
/*                  
/*                   F
/* 
/* pre-order traverse: A B D C E F
/* in-order traverse: D B A E F C
/* post-order traverse: D B F E C A
/* -----------------------------------------------------------------------------
/* 修改记录    :
/* 日 期        版本     修改人        修改内容
/* 2011/01/01   1.0      <xxx>         创建
/* </PRE>
*******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "common.h"

2.1 递归遍历
先根遍历二叉树的递归遍历算法描述为:

  在先序遍历中,对节点的访问工作是在它的左右儿子被访问之前进行的。换言之,先序遍历访问节点的顺序是根节点-左儿子-右儿子。由于树可以通过递归来定义,所以树的常见操作用递归实现常常是方便清晰的。递归实现的代码如下:

  由此,可以模仿递归算法写出非递归算法的遍历函数。

/******************************************************************************
/* 函数原型声明
/******************************************************************************/
Status Visit(TElemType e);
Status PreOrderTraverse(BiTree T, Status (*Visit)(TElemType));
Status InOrderTraverse(BiTree T, Status (*Visit)(TElemType));
Status PostOrderTraverse(BiTree T, Status (*Visit)(TElemType));

若二叉树为空,则算法结束;否则
1)输出根节点;
2)先根遍历左子树;
3)先根遍历右子树;

1 void PreOrderTraversal(BinTree BT)
2 {
3     if( BT ) 
4     {
5         printf(“%dn”, BT->Data);        //对节点做些访问比如打印         
6         PreOrderTraversal(BT->Left);     //访问左儿子
7         PreOrderTraversal(BT->Right);    //访问右儿子
8     }
9 }

  

/*******************************************************************************
/* <FUNC>
/* 函数名   : Visit
/* 功能     : 打印节点数据
/* 参数     : -
/* 返回值   : -
/* 备注     : -
/* 作者     : <xxx>
/* </FUNC>
*******************************************************************************/
Status Visit(TElemType e)
{
    printf("%c", e);
    return OK;
}

void PreOrder(BinaryTreeNode_t* pRoot)
{
BinaryTreeNode_t* p;

  由递归代码可以看出,该递归为尾递归(尾递归即递归形式在函数末尾或者说在函数即将返回前)。尾递归的递归调用需要用栈存储调用的信息,当数据规模较大时容易越出栈空间。虽然现在大部分的编译器能够自动去除尾递归,但是即使如此,我们不妨自己去除。非递归先序遍历算法基本思路:使用堆栈

 1 template <typename T> 2 void PreOrderTraverse(BiTNode & Tree, int (*Visit 3 { 4     InitStack; //初始化栈S 5     BiTree p;  6     Push; //根节点入栈 7     while (!StackEmpty 8     { 9         while (GetTop && p) 10         {11             Visit;12             Push(S,; //左子树压栈13         } //while14         Pop; //空指针出栈,用p返回其节点---------115         if (!StackEmpty16         {17             Pop; //-------------------------------218             Push(S,p.rchild);19         } //if20     } //while21 }

/*******************************************************************************
/* <FUNC>
/* 函数名   : PreOrderTraverse
/* 功能     : 前序遍历二叉树
/* 参数     : -
/* 返回值   : -
/* 备注     : 非递归法
/* 作者     : <xxx>
/* </FUNC>
*******************************************************************************/
Status PreOrderTraverse(BiTree T, Status (*Visit)(TElemType))
{
    LinkStack S;  BiTNode *p = NULL;
    InitStack(S); p = T;
    while (p || !StackEmpty(S)) {
        if (p) { //访问根结点, 遍历左子树
            if (!Visit(p->data)) return ERROR;
            Push(S, p); p = p->lchild; 
        }
        else { //根指针出栈, 遍历右子树
            Pop(S, p); 
            p = p->rchild;
        }//else
    }
    return OK;
}

p = pRoot;

  a. 遇到一个节点,访问它,然后把它压栈,并去遍历它的左子树;

  使用栈来管理遍历的过程,由于是先序遍历,所以每向左压一次栈时便Visit一次节点,到达最左端时(依次从根节点Visit了左子树各子树的根节点)向右压栈,到达右叶子节点时回退,由于第17行的退栈,会使得栈顶变为上上层的根节点。也就是遍历右子树时不需要保存当前层的根指针,可直接修改栈顶的指针。说的有些不清楚,但这和递归算法思路一致,不过是自己管理栈的进出。

/*******************************************************************************
/* <FUNC>
/* 函数名   : InOrderTraverse
/* 功能     : 中序遍历二叉树
/* 参数     : -
/* 返回值   : -
/* 备注     : 采用二叉链表存储结构, Visit是对数据元素操作的应用函数, 
/*            中序遍历二叉树T的非递归算法, 对每个数据元素调用函数Visit
/* 作者     : <xxx>
/* </FUNC>
*******************************************************************************/
Status InOrderTraverse(BiTree T, Status (*Visit)(TElemType e))
{
    LinkStack S;  BiTNode *p = NULL;
    InitStack(S); p = T;
    while (p || !StackEmpty(S)) {
        if (p) { Push(S, p); p = p->lchild; } //根指针进栈, 遍历左子树
        else { //根指针退栈, 访问根结点, 遍历右子树
            Pop(S, p); if (!Visit(p->data)) return ERROR;
            p = p->rchild;
        }//else
    }
    return OK;
}

if (p != NULL)
{
std::cout<<p->value<<" ";
PreOrder(p->pLeft);
PreOrder(p->pRight);
}
}

  b. 当左子树遍历结束后,从栈顶弹出该节点并将其指向右儿子,继续a步骤;

  注意,粗略一看可能会产生疑惑:Pop退栈p多退了一层。这里第一个Pop函数退出空指针,p是其返回值也为空,经过第二个Pop函数p才为有效栈顶。就比如,小红组装某个物件将步骤列了个清单,她将要着手去做这一步骤时,将该步骤从清单划去,划去的这一时刻是上一件事产生的影响。

/*******************************************************************************
/* <FUNC>
/* 函数名   : PostOrderTraverse
/* 功能     : 后序遍历二叉树
/* 参数     : -
/* 返回值   : -
/* 备注     : 非递归法
/* 作者     : <xxx>
/* </FUNC>
*******************************************************************************/
Status PostOrderTraverse(BiTree T, Status (*Visit)(TElemType))
{
    LinkStack S;  BiTNode *p = NULL; 
    BiTNode *top = NULL;   BiTNode *pre = NULL;
    InitStack(S); p = T;
    while (p || !StackEmpty(S)) {
        if (p) { //遍历左子树
            Push(S, p); p = p->lchild; 
        }
        else { //根据栈顶元素的右孩子属性及与前驱节点的关系访问根结点或遍历右子树
               //遍历结束条件: 栈为空且p为NULL
            GetTop(S, top);
            while (!StackEmpty(S) && (!top->rchild || top->rchild == pre)) {
                Pop(S, top);  pre = top; 
                if (!Visit(top->data)) return ERROR;
                GetTop(S, top);
            }
            p = StackEmpty(S) ? NULL : top->rchild;
        }//else
    }
    return OK;
}

2.2 非递归遍历
利用栈存储二叉树节点,算法思想为:
从二叉树根节点开始,沿左子树一直走到末端(左孩子为空)为止。在走的过程中,访问所遇节点,并依次把所遇节点进栈,当左子树为空时,从栈顶退出某节点,并将指针指向该节点的右孩子。如此重复,直到栈为空或指针为空止。
void PreOrder(const BinaryTreeNode_t* pRoot)
{
std::stack<BinaryTreeNode_t*> s;
BinaryTreeNode_t* p;
p = pRoot;

  c. 当所有节点访问完即最后访问的树节点为空且栈空时,停止。

本文由全球彩票平台发布于全球彩票注册平台编程,转载请注明出处:【全球彩票注册平台】二叉树非递归遍历,线索

TAG标签: 全球彩票平台
Ctrl+D 将本页面保存为书签,全面了解最新资讯,方便快捷。