深入遍历二叉树的各种操作详解(非递归遍历)

先使用先序的方法建立一棵二叉树,然后分别使用递归与非递归的方法实现前序、中序、后序遍历二叉树,并使用了两种方法来进行层次遍历二叉树,一种方法就是使用STL中的queue,另外一种方法就是定义了一个数组队列,分别使用了front和rear两个数组的下标来表示入队与出队,还有两个操作就是求二叉树的深度、结点数。。。


代码如下:

#include<iostream>
#include<queue>
#include<stack>
using namespace std;
//二叉树结点的描述
typedef struct BiTNode
{
    char data;
    struct BiTNode *lchild, *rchild;      //左右孩子
}BiTNode,*BiTree;
//按先序遍历创建二叉树
//BiTree *CreateBiTree()     //返回结点指针类型
//void CreateBiTree(BiTree &root)      //引用类型的参数
void CreateBiTree(BiTNode **root)    //二级指针作为函数参数
{
    char ch; //要插入的数据
    scanf("\n%c", &ch);
    //cin>>ch;
    if(ch=='#')
        *root = NULL;
    else
    {
        *root = (BiTNode *)malloc(sizeof(BiTNode));
        (*root)->data = ch;
        printf("请输入%c的左孩子:",ch);
        CreateBiTree(&((*root)->lchild));
        printf("请输入%c的右孩子:",ch);
        CreateBiTree(&((*root)->rchild));
    }
}
//前序遍历的算法程序
void PreOrder(BiTNode *root)
{
    if(root==NULL)
        return ;
    printf("%c ", root->data); //输出数据
    PreOrder(root->lchild); //递归调用,前序遍历左子树
    PreOrder(root->rchild); //递归调用,前序遍历右子树
}
//中序遍历的算法程序
void InOrder(BiTNode *root)
{
    if(root==NULL)
        return ;
    InOrder(root->lchild); //递归调用,前序遍历左子树
    printf("%c ", root->data); //输出数据
    InOrder(root->rchild); //递归调用,前序遍历右子树
}
//后序遍历的算法程序
void PostOrder(BiTNode *root)
{
    if(root==NULL)
        return ;
    PostOrder(root->lchild);      //递归调用,前序遍历左子树
    PostOrder(root->rchild);      //递归调用,前序遍历右子树
    printf("%c ", root->data);    //输出数据  
}
/*
二叉树的非递归前序遍历,前序遍历思想:先让根进栈,只要栈不为空,就可以做弹出操作,
每次弹出一个结点,记得把它的左右结点都进栈,记得右子树先进栈,这样可以保证右子树在栈中总处于左子树的下面。
*/
void PreOrder_Nonrecursive2(BiTree T)     //先序遍历的非递归  
{
    if(!T)  
        return ;  
 
    stack<BiTree> s;
    s.push(T);
    while(!s.empty())
    {
        BiTree temp = s.top();
        cout<<temp->data<<" ";
        s.pop();
        if(temp->rchild)
            s.push(temp->rchild);
        if(temp->lchild)
            s.push(temp->lchild);
    }
}
void PreOrder_Nonrecursive(BiTree T)     //先序遍历的非递归
{
    if(!T)
        return ;
    stack<BiTree> s;
    while(T)          // 左子树上的节点全部压入到栈中
    {
        s.push(T);
        cout<<T->data<<"  ";
        T = T->lchild;
    }
    
    while(!s.empty())
    {        
        BiTree temp = s.top()->rchild;  // 栈顶元素的右子树
        s.pop();                        // 弹出栈顶元素
        while(temp)          // 栈顶元素存在右子树,则对右子树同样遍历到最下方
        {
            cout<<temp->data<<"  ";
            s.push(temp);
            temp = temp->lchild;
        }
    }
}
void InOrderTraverse(BiTree T)   // 中序遍历的非递归
{
    if(!T)
        return ;
    stack<BiTree> S;
    BiTree curr = T->lchild;    // 指向当前要检查的节点
    S.push(T);
    while(curr != NULL || !S.empty())
    {
        while(curr != NULL)    // 一直向左走
        {
            S.push(curr);
            curr = curr->lchild;
        }
        curr = S.top();
        S.pop();
        cout<<curr->data<<"  ";
        curr = curr->rchild;
    }
}
void PostOrder_Nonrecursive(BiTree T)  // 后序遍历的非递归  
{  
    stack<BiTree> S;  
    BiTree curr = T ;           // 指向当前要检查的节点
    BiTree previsited = NULL;    // 指向前一个被访问的节点
    while(curr != NULL || !S.empty())  // 栈空时结束  
    {  
        while(curr != NULL)            // 一直向左走直到为空
        {  
            S.push(curr);  
            curr = curr->lchild;  
        }  
        curr = S.top();
        // 当前节点的右孩子如果为空或者已经被访问,则访问当前节点
        if(curr->rchild == NULL || curr->rchild == previsited)  
        {  
            cout<<curr->data<<"  ";  
            previsited = curr;  
            S.pop();  
            curr = NULL;  
        }  
        else
            curr = curr->rchild;      // 否则访问右孩子
    }  
}
void PostOrder_Nonrecursive(BiTree T)  // 后序遍历的非递归     双栈法
{  
    stack<BiTree> s1 , s2;  
    BiTree curr ;           // 指向当前要检查的节点
    s1.push(T);
    while(!s1.empty())  // 栈空时结束  
    {
        curr = s1.top();
        s1.pop();
        s2.push(curr);
        if(curr->lchild)
            s1.push(curr->lchild);
        if(curr->rchild)
            s1.push(curr->rchild);
    }
    while(!s2.empty())
    {
        printf("%c ", s2.top()->data);
        s2.pop();
    }
}
int visit(BiTree T)
{
    if(T)
    {
        printf("%c ",T->data);
        return 1;
    }
    else
        return 0;
}
void LeverTraverse(BiTree T)   //方法一、非递归层次遍历二叉树
{
    queue <BiTree> Q;
    BiTree p;
    p = T;
    if(visit(p)==1)
        Q.push(p);
    while(!Q.empty())
    {
        p = Q.front();
        Q.pop();
        if(visit(p->lchild) == 1)
            Q.push(p->lchild);
        if(visit(p->rchild) == 1)
            Q.push(p->rchild);
    }
}
void LevelOrder(BiTree BT)     //方法二、非递归层次遍历二叉树
{
    BiTNode *queue[10];//定义队列有十个空间
    if (BT==NULL)
        return;
    int front,rear;
    front=rear=0;
    queue[rear++]=BT;
    while(front!=rear)//如果队尾指针不等于对头指针时
    {
        cout<<queue[front]->data<<"  ";  //输出遍历结果
        if(queue[front]->lchild!=NULL)  //将队首结点的左孩子指针入队列
        {
            queue[rear]=queue[front]->lchild;
            rear++;    //队尾指针后移一位
        }
        if(queue[front]->rchild!=NULL)
        {
            queue[rear]=queue[front]->rchild;    //将队首结点的右孩子指针入队列
            rear++;   //队尾指针后移一位
        }
        front++;    //对头指针后移一位
    }
}
int depth(BiTNode *T)   //树的深度
{
    if(!T)
        return 0;
    int d1,d2;
    d1=depth(T->lchild);
    d2=depth(T->rchild);
    return (d1>d2?d1:d2)+1;
    //return (depth(T->lchild)>depth(T->rchild)?depth(T->lchild):depth(T->rchild))+1;
}
int CountNode(BiTNode *T)
{
    if(T == NULL)
        return 0;
    return 1+CountNode(T->lchild)+CountNode(T->rchild);
}
int main(void)
{
    BiTNode *root=NULL; //定义一个根结点
    int flag=1,k;
    printf("                     本程序实现二叉树的基本操作。\n");
    printf("可以进行建立二叉树,递归先序、中序、后序遍历,非递归先序、中序遍历及非递归层序遍历等操作。\n");
    while(flag)
    {
        printf("\n");
        printf("|--------------------------------------------------------------|\n");
        printf("|                    二叉树的基本操作如下:                     |\n");
        printf("|                        0.创建二叉树                          |\n");
        printf("|                        1.递归先序遍历                        |\n");
        printf("|                        2.递归中序遍历                        |\n");
        printf("|                        3.递归后序遍历                        |\n");
        printf("|                        4.非递归先序遍历                      |\n");
        printf("|                        5.非递归中序遍历                      |\n");
        printf("|                        6.非递归后序遍历                      |\n");
        printf("|                        7.非递归层序遍历                      |\n");
        printf("|                        8.二叉树的深度                        |\n");
        printf("|                        9.二叉树的结点个数                    |\n");
        printf("|                        10.退出程序                            |\n");
        printf("|--------------------------------------------------------------|\n");
        printf("                        请选择功能:");
        scanf("%d",&k);
        switch(k)
        {
        case 0:
            printf("请建立二叉树并输入二叉树的根节点:");
            CreateBiTree(&root);
            break;
        case 1:
            if(root)
            {
                printf("递归先序遍历二叉树的结果为:");
                PreOrder(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 2:
            if(root)
            {
                printf("递归中序遍历二叉树的结果为:");
                InOrder(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 3:
            if(root)
            {
                printf("递归后序遍历二叉树的结果为:");
                PostOrder(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 4:
            if(root)
            {
                printf("非递归先序遍历二叉树:");
                PreOrder_Nonrecursive(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 5:
            if(root)
            {
                printf("非递归中序遍历二叉树:");
                InOrderTraverse(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 6:
            if(root)
            {
                printf("非递归后序遍历二叉树:");
                PostOrder_Nonrecursive(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 7:
            if(root)
            {
                printf("非递归层序遍历二叉树:");
                //LeverTraverse(root);
                LevelOrder(root);
                printf("\n");
            }
            else
                printf("          二叉树为空!\n");
            break;
        case 8:
            if(root)
                printf("这棵二叉树的深度为:%d\n",depth(root));
            else
                printf("          二叉树为空!\n");
            break;
        case 9:
            if(root)
                printf("这棵二叉树的结点个数为:%d\n",CountNode(root));
            else
                printf("          二叉树为空!\n");
            break;
        default:
            flag=0;
            printf("程序运行结束,按任意键退出!\n");
        }
    }
    system("pause");
    return 0;
}

运行效果图如下:

分别输入:
1
2
4
#
#
5
#
#
3
6
#
#
7
#
#
就可以构造如下图所示的二叉树了。。

后序遍历非递归的另外一种写法:


代码如下:

/*
    后序遍历由于遍历父节点是在遍历子节点之后,而且左节点和右节点遍历后的行为不一样,
    所以需要用变量来记录前一次访问的节点,根据前一次节点和现在的节点的关系来确定具体执行什么操作
    */ 
    void Postorder(BiTree T) 
    { 
        if(T == NULL) 
            return ; 
        stack<BiTree> s; 
        BiTree prev = NULL , curr = NULL; 
        s.push(T); 
        while(!s.empty()) 
        { 
            curr = s.top(); 
            if(prev == NULL  || prev->lchild == curr || prev->rchild == curr) 
            { 
                if(curr->lchild != NULL) 
                    s.push(curr->lchild); 
                else if(curr->rchild != NULL) 
                    s.push(curr->rchild); 
            } 
            else if(curr->lchild == prev) 
            { 
                if(curr->rchild != NULL) 
                    s.push(curr->rchild); 
            } 
            else 
            { 
                cout<<curr->data; 
                s.pop(); 
            } 
            prev = curr; 
        } 
    }

输入二叉树中的两个节点,输出这两个结点在数中最低的共同父节点。
思路:遍历二叉树,找到一条从根节点开始到目的节点的路径,然后在两条路径上查找共同的父节点。


代码如下:

// 得到一条从根节点开始到目的节点的路径 
    bool GetNodePath(TreeNode *pRoot , TreeNode *pNode , vector<TreeNode *> &path) 
    { 
        if(pRoot == NULL) 
            return false; 
        if(pRoot == pNode) 
            return true; 
        else if(GetNodePath(pRoot->lchild , pNode , path) ) 
        { 
            path.push_back(pRoot->lchild); 
            return true; 
        } 
        else if(GetNodePath(pRoot->rchild , pNode , path) ) 
        { 
            path.push_back(pRoot->rchild); 
            return true; 
        } 
        return false; 
    } 
    TreeNode *GetLastCommonNode(const vector<TreeNode *> &path1 , const vector<TreeNode *> &path2) 
    { 
        vector<TreeNode *>::const_iterator iter1 = path1.begin(); 
        vector<TreeNode *>::const_iterator iter2 = path2.begin(); 
        TreeNode *pLast; 
        while(iter1 != path1.end() && iter2 != path2.end() ) 
        { 
            if(*iter1 == *iter2) 
                pLast = *iter1; 
            else 
                break; 
            iter1++; 
            iter2++; 
        } 
        return pLast; 
    } 
    TreeNode *GetLastCommonParent(TreeNode *pRoot , TreeNode *pNode1 , TreeNode *pNode2) 
    { 
        if(pRoot == NULL || pNode1 == NULL || pNode2 == NULL) 
            return  NULL; 
        vector<TreeNode *> path1; 
        GetNodePath(pRoot , pNode1 , path1);

vector<TreeNode *> path2; 
        GetNodePath(pRoot , pNode2 , path2); 
        return GetLastCommonNode(path1 , path2); 
    }

(0)

相关推荐

  • c语言版本二叉树基本操作示例(先序 递归 非递归)

    复制代码 代码如下: 请按先序遍历输入二叉树元素(每个结点一个字符,空结点为'='):ABD==E==CF==G== 先序递归遍历:A B D E C F G中序递归遍历:D B E A F C G后序递归遍历:D E B F G C A层序递归遍历:ABCDEFG先序非递归遍历:A B D E C F G中序非递归遍历:D B E A F C G后序非递归遍历:D E B F G C A深度:请按任意键继续. . . 复制代码 代码如下: #include<stdio.h>#include&

  • C++基于递归和非递归算法求二叉树镜像的方法

    本文实例讲述了C++基于递归和非递归算法求二叉树镜像的方法.分享给大家供大家参考,具体如下: /*求二叉树镜像 -- 采用递归和非递归方法 经调试可运行源码及分析如下: ***/ #include <stdlib.h> #include <iostream> #include <queue> using std::cout; using std::cin; using std::endl; using std::queue; /*二叉树结点定义*/ typedef st

  • 二叉树先序遍历的非递归算法具体实现

    在前面一文,说过二叉树的递归遍历算法(二叉树先根(先序)遍历的改进),此文主要讲二叉树的非递归算法,采用栈结构 总结先根遍历得到的非递归算法思想如下: 1)入栈,主要是先头结点入栈,然后visit此结点 2)while,循环遍历当前结点,直至左孩子没有结点 3)if结点的右孩子为真,转入1)继续遍历,否则退出当前结点转入父母结点遍历转入1) 先看符合此思想的算法: 复制代码 代码如下: int PreOrderTraverseNonRecursiveEx(const BiTree &T, int

  • C++非递归建立二叉树实例

    本文实例讲述了C++非递归建立二叉树的方法.分享给大家供大家参考.具体分析如下: 思路: 设置一个标记变量flag并初始化为1. flag = 1表示现在需要创建当前结点的左孩子,2表示需要创建右孩子,3则表示当前结点的左右孩子都已经创建完毕,需要执行出栈操作,直到当前结点不是父结点的右孩子为止. 以先序创建如图所示二杈树: 实现代码: PBTree create() { char ch[20]; scanf("%s",ch); int len = strlen(ch); PBTree

  • C#非递归先序遍历二叉树实例

    本文实例讲述了C#非递归先序遍历二叉树的方法.分享给大家供大家参考.具体如下: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace ConsoleApplication5 { class Program { static void Main(string[] args) { Node treeRoo

  • C++使用递归和非递归算法实现的二叉树叶子节点个数计算方法

    本文实例讲述了C++使用递归和非递归算法实现的二叉树叶子节点个数计算方法.分享给大家供大家参考,具体如下: /*求二叉树叶子节点个数 -- 采用递归和非递归方法 经调试可运行源码及分析如下: ***/ #include <stdlib.h> #include <iostream> #include <stack> using std::cout; using std::cin; using std::endl; using std::stack; /*二叉树结点定义*/

  • 数据结构 二叉树的递归与非递归

    数据结构 二叉树的递归与非递归 实例代码: #include <iostream> #include <queue> #include <stack> #include <assert.h> using namespace std; template<class T> struct BinaryTreeNode { BinaryTreeNode<T>* _left; BinaryTreeNode<T>* _right; T

  • 二叉树前序遍历的非递归算法

    二叉树的前序遍历是先根节点,然后如果有左子树则再先序遍历左子树,然后如果有右子树则再先序遍历其又子树.递归算法如下 复制代码 代码如下: void   preorder(Betree *t){   if(t==null) return;visit(t);//访问该节点preorder(t->lchild);preorder(t->rchild); } 当然递归算法是隐式使用了栈.我们仔细分析这个过程,先是取出了根节点进行了访问,然后我们把根节点退栈,退栈后必然有节点进栈,怎么办呢?根节点只能直

  • 深入遍历二叉树的各种操作详解(非递归遍历)

    先使用先序的方法建立一棵二叉树,然后分别使用递归与非递归的方法实现前序.中序.后序遍历二叉树,并使用了两种方法来进行层次遍历二叉树,一种方法就是使用STL中的queue,另外一种方法就是定义了一个数组队列,分别使用了front和rear两个数组的下标来表示入队与出队,还有两个操作就是求二叉树的深度.结点数... 复制代码 代码如下: #include<iostream>#include<queue>#include<stack>using namespace std;/

  • C语言二叉树常见操作详解【前序,中序,后序,层次遍历及非递归查找,统计个数,比较,求深度】

    本文实例讲述了C语言二叉树常见操作.分享给大家供大家参考,具体如下: 一.基本概念 每个结点最多有两棵子树,左子树和右子树,次序不可以颠倒. 性质: 1.非空二叉树的第n层上至多有2^(n-1)个元素. 2.深度为h的二叉树至多有2^h-1个结点. 满二叉树:所有终端都在同一层次,且非终端结点的度数为2. 在满二叉树中若其深度为h,则其所包含的结点数必为2^h-1. 完全二叉树:除了最大的层次即成为一颗满二叉树且层次最大那层所有的结点均向左靠齐,即集中在左面的位置上,不能有空位置. 对于完全二叉

  • JavaScript实现二叉树定义、遍历及查找的方法详解

    本文实例讲述了JavaScript实现二叉树定义.遍历及查找的方法.分享给大家供大家参考,具体如下: 二叉树(binary tree) 在写这篇文章之前说一下数据结构和算法这个系列,这个系列包含了很多东西,比如啥子排序,线性表,广义表,树,图这些大家都是知道的,但是这些东西我们学了之后工作中能用到的又有多少呢,据我所知绝大部分公司,一线码农,屌丝,程序猿是用不到这些东西,既然这样为啥子我还要强调这个系列呢,本人觉得算法和数据结构是程序的基本功,前提想脱离一线码农,普通程序猿行列,说得通俗一点就是

  • JavaScript二叉搜索树构建操作详解

    目录 前言 什么是二叉搜索树 构建一颗二叉搜索树 二叉搜索树的操作 向二叉搜索树中插入数据 查找二叉搜索树中的数据 删除二叉搜索树的某个节点 前驱后继节点 删除一个节点的三种情况 实现代码 完整代码 总结 前言 前面我们介绍了二叉树这个数据结构以及二叉树的遍历算法,这篇文章我们来学习一下一个特殊的二叉树——二叉搜索树(BST Binary Search Tree),也叫二叉排序树.二叉查找树. 什么是二叉搜索树 二叉搜索树首先它是一棵二叉树,而且还满足下面这些特质: 对于任何一个非空节点来说,它

  • jQuery原理系列-常用Dom操作详解

    1. 事件绑定$(el).bind ie使用attachEvent,其它浏览器使用addEventListener,不同的是ie多了个on前缀,this绑定在window上,需要用call和apply修正this 的指向. if (element.addEventListener) { element.addEventListener(type, handler, useCapture); } else { if (element.attachEvent) { element.attachEve

  • Java中对List集合的常用操作详解

    目录: 1.list中添加,获取,删除元素: 2.list中是否包含某个元素: 3.list中根据索引将元素数值改变(替换): 4.list中查看(判断)元素的索引: 5.根据元素索引位置进行的判断: 6.利用list中索引位置重新生成一个新的list(截取集合): 7.对比两个list中的所有元素: 8.判断list是否为空: 9.返回Iterator集合对象: 10.将集合转换为字符串: 11.将集合转换为数组: 12.集合类型转换: 备注:内容中代码具有关联性. 1.list中添加,获取,

  • Java中map遍历方式的选择问题详解

    1. 阐述 对于Java中Map的遍历方式,很多文章都推荐使用entrySet,认为其比keySet的效率高很多.理由是:entrySet方法一次拿到所有key和value的集合:而keySet拿到的只是key的集合,针对每个key,都要去Map中额外查找一次value,从而降低了总体效率.那么实际情况如何呢? 为了解遍历性能的真实差距,包括在遍历key+value.遍历key.遍历value等不同场景下的差异,我试着进行了一些对比测试. 2. 对比测试 一开始只进行了简单的测试,但结果却表明k

  • Android利用Dom对XML进行增删改查操作详解

    1. 概述 平常我们一般是使用JSON与服务器做数据通信,JSON的话,直接用GSON或者其他库去解析很简单.但是,其他有些服务器会返回XML格式的文件,这时候就需要去读取XML文件了. XML的解析有三种方式,在Android中提供了三种解析XML的方式:DOM(Document Objrect Model) , SAX(Simple API XML) ,以及Android推荐的Pull解析方式,他们也各有弊端,而这里来看看使用DOM的方式. 2. Dom解析 DOM解析器在解析XML文档时,

  • 对python for 文件指定行读写操作详解

    1.os.mknod("test.txt") #创建空文件 2.fp = open("test.txt",w) #直接打开一个文件,如果文件不存在则创建文件 3.关于open 模式: 详情: w:以写方式打开, a:以追加模式打开 (从 EOF 开始, 必要时创建新文件) r+:以读写模式打开 w+:以读写模式打开 (参见 w ) a+:以读写模式打开 (参见 a ) rb:以二进制读模式打开 wb:以二进制写模式打开 (参见 w ) ab:以二进制追加模式打开 (

  • Java Iterator接口遍历单列集合迭代器原理详解

    这篇文章主要介绍了Java Iterator接口遍历单列集合迭代器原理详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Iterator接口概述 在程序开发中,经常需要遍历集合中的所有元素.针对这种需求,JDK专门提供了一个接口java.util.Iterator . Iterator 接口也是Java集合中的一员,但它与 Collection . Map 接口有所不同,Collection 接口与 Map 接口主要用于存储元素,而 Iter

随机推荐