关闭

[置顶] 二叉树的构建及其遍历算法

标签: 二叉树二叉树遍历数据结构
473人阅读 评论(0) 收藏 举报
分类:

本篇博客参照了兰亭风雨的博客:http://blog.csdn.net/ns_code/article/details/12977901/

概要

二叉树是一种非常重要的数据结构,很多其他数据机构都是基于二叉树的基础演变过来的。二叉树有先、中、后,层次四种遍历方式,因为树的本身就是用递归定义的,因此采用递归的方法实现三种遍历,不仅代码简洁且容易理解,但其开销也比较大,而若采用非递归方法实现先中后3种遍历,则要用栈来模拟实现(递归也是用栈实现的)。下面先简要介绍先中后三种遍历方式的递归实现,再详细介绍先中后三种遍历方式的非递归实现与层次遍历。


递归先序遍历

先序遍历按照“根节点->左子树->右子树”的顺序进行遍历。代码如下:

    void PreorderTraversal(BinaryTree* T){
            if(T == NULL){
                return;
            }
            cout<<T->data<<" ";                                             //访问根节点并输出 
            T->PreorderTraversal(T->lchild);                                //递归前序遍历左子树 
            T->PreorderTraversal(T->rchild);                                //递归前序遍历右子树
        }

递归中序遍历

中序遍历按照“左子树->根节点->右子树”的顺序进行遍历。代码如下:

void InorderTraversal(BinaryTree* T){
            if(T == NULL){
                return;
            }
            T->InorderTraversal(T->lchild);                             //递归中序遍历左子树 
            cout<<T->data<<" ";                                         //访问根节点并输出 
            T->InorderTraversal(T->rchild);                             //递归中序遍历左子树 
        }

递归后序遍历

后序遍历按照“左子树->右子树->根结点”的顺序进行遍历。代码如下:

void PostorderTraversal(BinaryTree* T){
            if(T == NULL){
                return;
            }
            T->PostorderTraversal(T->lchild);                       //递归后序遍历左子树 
            T->PostorderTraversal(T->rchild);                       //递归后序遍历右子树 
            cout<<T->data<<" ";                                     //访问并打印根节点 
        }

非递归先序遍历

非递归的实现思路如下:
对于任一节点P,
1)输出节点P,然后将其入栈,再看P的左孩子是否为空;
2)若P的左孩子不为空,则置P的左孩子为当前节点,重复1)的操作;
3)若P的左孩子为空,则将栈顶节点出栈,但不输出,并将出栈节点的右孩子置为当前节点,看其是否为空;
4)若不为空,则循环至1)操作;
5)如果为空,则继续出栈,但不输出,同时将出栈节点的右孩子置为当前节点,看其是否为空,重复4)和5)操作;
6)直到当前节点P为NULL并且栈空,遍历结束。
代码如下:

void PreorderTraversal2(BinaryTree* T){
            stack<BinaryTree*> stack;                                       //初始化栈 
            BinaryTree* binary_tree_curr = T;                               //保存当前结点 
            //当前结点为空跳出循环 
            while(binary_tree_curr || !stack.empty()){
                cout<<binary_tree_curr->data<<" ";                          //打印当前结点 
                stack.push(binary_tree_curr);                               //当前结点入栈 
                binary_tree_curr = binary_tree_curr->lchild;                //访问左子树 
                //当前结点为空为空,当前结点出栈
                //并把右孩子作为当前结点 
                while(!binary_tree_curr && !stack.empty()){                 
                    binary_tree_curr = stack.top();
                    stack.pop();
                    binary_tree_curr = binary_tree_curr->rchild;
                }
            }
        }

非递归中序遍历

非递归的实现思路如下:
对于任一节点P,
1)若P的左孩子不为空,则将P入栈并将P的左孩子置为当前节点,然后再对当前节点进行相同的处理;
2)若P的左孩子为空,则输出P节点,而后将P的右孩子置为当前节点,看其是否为空;
3)若不为空,则重复1)和2)的操作;
4)若为空,则执行出栈操作,输出栈顶节点,并将出栈的节点的右孩子置为当前节点,看起是否为空,重复3)和4)的操作;
5)直到当前节点P为NULL并且栈为空,则遍历结束。
代码如下:

void InorderTraversal2(BinaryTree* T){
            stack<BinaryTree*> stack;                                   //初始化栈 
            BinaryTree* binary_tree_curr = T;                           //保存当前结点 
            while(binary_tree_curr || !stack.empty()){
                if(binary_tree_curr->lchild){                           //左孩子非空
                    stack.push(binary_tree_curr);                       //当前结点入栈 
                    binary_tree_curr = binary_tree_curr->lchild;        //遍历左子树 
                }else{
                    //左孩子为空,则打印当前结点遍历右子树 
                    cout<<binary_tree_curr->data<<" ";                  
                    binary_tree_curr = binary_tree_curr->rchild;
                    //如果为空,且栈不空,则将栈顶节点出栈,并输出该节点,  
                    //同时将它的右孩子设为当前节点,继续判断,直到当前节点不为空   
                    while(!binary_tree_curr && !stack.empty()){
                        binary_tree_curr = stack.top();
                        cout<<binary_tree_curr->data<<" ";
                        stack.pop();
                        binary_tree_curr = binary_tree_curr->rchild;
                    }
                }
            }
        }

非递归后序遍历

思路如下:
对于任一节点P,
1)先将节点P入栈;
2)若P不存在左孩子和右孩子,或者P存在左孩子或右孩子,但左右孩子已经被输出,则可以直接输出节点P,并将其出栈,将出栈节点P标记为上一个输出的节点,再将此时的栈顶结点设为当前节点;
3)若不满足2)中的条件,则将P的右孩子和左孩子依次入栈,当前节点重新置为栈顶结点,之后重复操作2);
4)直到栈空,遍历结束。
代码如下:

    //非递归后序遍历
        void PostorderTraversal2(BinaryTree* T){
            stack<BinaryTree*> stack;
            BinaryTree* binary_tree_curr = T;                   //当前结点 
            BinaryTree* binary_tree_pre = NULL;                // 上一个结点 
            //先将树的根节点入栈
            stack.push(binary_tree_curr);
            //直到栈空时,结束循环  
            while(!stack.empty()){
                binary_tree_curr = stack.top();              //当前节点置为栈顶节点  
                //如果当前节点没有左右孩子,或者有左孩子或有孩子,但已经被访问输出,  
                //则直接输出该节点,将其出栈,将其设为上一个访问的节点  
                if((binary_tree_curr->lchild == NULL && binary_tree_curr->rchild == NULL) ||
                    (binary_tree_curr != NULL && binary_tree_curr->lchild == binary_tree_pre || 
                                                binary_tree_curr->rchild == binary_tree_pre)){
                    cout<<binary_tree_curr->data<<" ";
                    stack.pop();
                    binary_tree_pre = binary_tree_curr; 
                }else{
                    //如果不满足上面两种情况,则将其右孩子左孩子依次入栈  
                    if(binary_tree_curr->rchild != NULL){
                        stack.push(binary_tree_curr->rchild);
                    }
                    if(binary_tree_curr->lchild != NULL){
                        stack.push(binary_tree_curr->lchild);
                    } 
                }
            }
        }

层次遍历

层次遍历是指按照从从上到下,从左到右的顺序对二叉树的每一层进行遍历。思路如下:
对于任何结点P
1)首先将其入队,判断左右结点是否为空,如不是依次入队(先做孩子后右孩子)
2)把队列头元素出队,打印结点
3)重复1),2)两个步骤直至队列为空
代码如下:

        //层次遍历
        void LevelOrderTraversal(BinaryTree* T){
            queue<BinaryTree*> queue;
            BinaryTree* cur = T;
            //头结点入队 
            queue.push(cur);
            //队列为空时循环结束 
            while(!queue.empty()){
                //队列头元素出队 
                cur = queue.front();
                queue.pop();
                cout<<cur->data<<" ";
                //左孩子不为空入队 
                if(cur->lchild != NULL){
                    queue.push(cur->lchild);
                }
                //右孩子不为空时入队 
                if(cur->rchild != NULL){
                    queue.push(cur->rchild);
                }
            } 
        }

整体代码:

#include <iostream>
#include <stack> 
#include <queue>
using namespace std;

class BinaryTree{
    private:
        char data;
        BinaryTree* lchild;
        BinaryTree* rchild;
    public: 
        //二叉树的初始化函数 
        BinaryTree* Create_BinaryTree(){
            BinaryTree* T = new BinaryTree;
            char ch;
            cin>>ch;
            if(ch == '#'){                                                  //“#”是结束标志 
                T = NULL;
            }else{
                T->data = ch;                                               //对当前结点初始化 
                T->lchild = Create_BinaryTree();                            //递归构造左子树 
                T->rchild = Create_BinaryTree();                            //递归构造右子树 
            }
            return T;
        }

        //递归前序遍历 
        void PreorderTraversal(BinaryTree* T){
            if(T == NULL){
                return;
            }
            cout<<T->data<<" ";                                             //访问根节点并输出 
            T->PreorderTraversal(T->lchild);                                //递归前序遍历左子树 
            T->PreorderTraversal(T->rchild);                                //递归前序遍历右子树
        }

        //非递归前序遍历 
        void PreorderTraversal2(BinaryTree* T){
            stack<BinaryTree*> stack;                                       //初始化栈 
            BinaryTree* binary_tree_curr = T;                               //保存当前结点 
            //当前结点为空跳出循环 
            while(binary_tree_curr || !stack.empty()){
                cout<<binary_tree_curr->data<<" ";                          //打印当前结点 
                stack.push(binary_tree_curr);                               //当前结点入栈 
                binary_tree_curr = binary_tree_curr->lchild;                //访问左子树 
                //当前结点为空为空,当前结点出栈
                //并把右孩子作为当前结点 
                while(!binary_tree_curr && !stack.empty()){                 
                    binary_tree_curr = stack.top();
                    stack.pop();
                    binary_tree_curr = binary_tree_curr->rchild;
                }
            }
        }

        //递归中序遍历 
        void InorderTraversal(BinaryTree* T){
            if(T == NULL){
                return;
            }
            T->InorderTraversal(T->lchild);                             //递归中序遍历左子树 
            cout<<T->data<<" ";                                         //访问根节点并输出 
            T->InorderTraversal(T->rchild);                             //递归中序遍历左子树 
        }

        //非递归中序遍历
        void InorderTraversal2(BinaryTree* T){
            stack<BinaryTree*> stack;                                   //初始化栈 
            BinaryTree* binary_tree_curr = T;                           //保存当前结点 
            while(binary_tree_curr || !stack.empty()){
                if(binary_tree_curr->lchild){                           //左孩子非空
                    stack.push(binary_tree_curr);                       //当前结点入栈 
                    binary_tree_curr = binary_tree_curr->lchild;        //遍历左子树 
                }else{
                    //左孩子为空,则打印当前结点遍历右子树 
                    cout<<binary_tree_curr->data<<" ";                  
                    binary_tree_curr = binary_tree_curr->rchild;
                    //如果为空,且栈不空,则将栈顶节点出栈,并输出该节点,  
                    //同时将它的右孩子设为当前节点,继续判断,直到当前节点不为空   
                    while(!binary_tree_curr && !stack.empty()){
                        binary_tree_curr = stack.top();
                        cout<<binary_tree_curr->data<<" ";
                        stack.pop();
                        binary_tree_curr = binary_tree_curr->rchild;
                    }
                }
            }
        }

        //递归后序遍历 
        void PostorderTraversal(BinaryTree* T){
            if(T == NULL){
                return;
            }
            T->PostorderTraversal(T->lchild);                       //递归后序遍历左子树 
            T->PostorderTraversal(T->rchild);                       //递归后序遍历右子树 
            cout<<T->data<<" ";                                     //访问并打印根节点 
        }

        //非递归后序遍历
        void PostorderTraversal2(BinaryTree* T){
            stack<BinaryTree*> stack;
            BinaryTree* binary_tree_curr = T;                   //当前结点 
            BinaryTree* binary_tree_pre = NULL;                // 上一个结点 
            //先将树的根节点入栈
            stack.push(binary_tree_curr);
            //直到栈空时,结束循环  
            while(!stack.empty()){
                binary_tree_curr = stack.top();              //当前节点置为栈顶节点  
                //如果当前节点没有左右孩子,或者有左孩子或有孩子,但已经被访问输出,  
                //则直接输出该节点,将其出栈,将其设为上一个访问的节点  
                if((binary_tree_curr->lchild == NULL && binary_tree_curr->rchild == NULL) ||
                    (binary_tree_curr != NULL && binary_tree_curr->lchild == binary_tree_pre || 
                                                binary_tree_curr->rchild == binary_tree_pre)){
                    cout<<binary_tree_curr->data<<" ";
                    stack.pop();
                    binary_tree_pre = binary_tree_curr; 
                }else{
                    //如果不满足上面两种情况,则将其右孩子左孩子依次入栈  
                    if(binary_tree_curr->rchild != NULL){
                        stack.push(binary_tree_curr->rchild);
                    }
                    if(binary_tree_curr->lchild != NULL){
                        stack.push(binary_tree_curr->lchild);
                    } 
                }
            }
        }

        //层次遍历
        void LevelOrderTraversal(BinaryTree* T){
            queue<BinaryTree*> queue;
            BinaryTree* cur = T;
            //头结点入队 
            queue.push(cur);
            //队列为空时循环结束 
            while(!queue.empty()){
                //队列头元素出队 
                cur = queue.front();
                queue.pop();
                cout<<cur->data<<" ";
                //左孩子不为空入队 
                if(cur->lchild != NULL){
                    queue.push(cur->lchild);
                }
                //右孩子不为空时入队 
                if(cur->rchild != NULL){
                    queue.push(cur->rchild);
                }
            } 
        }

        //二叉树的高度
        int getBinaryTreeHeight(BinaryTree* T){
            if(T){
                //递归求左子树高度 
                int lheight = T->getBinaryTreeHeight(T->lchild);
                //递归求右子树高度 
                int rheight = T->getBinaryTreeHeight(T->rchild);
                //树的高度等于左右子树高度的较大者加1
                int height = (lheight>rheight)?lheight:rheight;
                height++;
                return height;  
            }
            return 0; 
        }
};

int main()
{
    cout<<"请初始化二叉树:"<<endl;
    BinaryTree* T;
    T = T->Create_BinaryTree();

    cout<<"前序遍历(递归):"<<endl;
    T->PreorderTraversal(T);
    cout<<endl;
    cout<<"前序遍历(非递归):"<<endl;
    T->PreorderTraversal2(T);
    cout<<endl; 

    cout<<"中序遍历(递归):"<<endl;
    T->InorderTraversal(T);
    cout<<endl;
    cout<<"中序遍历(非递归):"<<endl;
    T->InorderTraversal2(T);
    cout<<endl; 

    cout<<"后序遍历(递归):"<<endl;
    T->PostorderTraversal(T);
    cout<<endl;
    cout<<"后序遍历(非递归):"<<endl;
    T->PostorderTraversal2(T);
    cout<<endl; 

    cout<<"层次遍历:"<<endl;
    T->LevelOrderTraversal(T);
    cout<<endl; 

    cout<<"二叉树高度为:" <<endl;
    cout<<T->getBinaryTreeHeight(T)<<endl;

    return 0;
 } 

下面的程序结果都是基于如下的二叉树进行的:
这里写图片描述
截图:
这里写图片描述

1
0
查看评论

数据结构与算法——二叉树的创建与遍历

这两天在看树的部分,先总结一下二叉树。 什么是树? 树也是一种数据结构,是由n个结点组成的具有层次关系的集合。树由根结点和子节点组成,与现实生活中的树不同,这里的树,根结点是在最上面的,叶子结点在下面,就像是将现实中的树倒着挂起来一样。 树的特点: 每个结点有零个或多个子结点。每一个...
  • hsk256
  • hsk256
  • 2015-05-23 13:33
  • 2926

二叉树的创建,遍历,查找算法及其程序实现(傻瓜版)

前一个月学习了一下二叉树,现在和大家分享一下。(高手勿看) 二叉树常被用于实现二叉查找树和二叉堆。值得注意的是,二叉树不是树的特殊情形。在图论中,二叉树是一个连通的无环图,并且每一个顶点的度不大于3。有根二叉树还要满足根结点的度不大于2。有了根结点后,每个顶点定义了唯一的根结点,和最多2个子结点。...
  • zqg810832803
  • zqg810832803
  • 2012-08-25 09:03
  • 1121

java实现二叉树的创建及5种遍历

用java实现的数组创建二叉树以及先序遍历,中序遍历,后序遍历三种遍历
  • ls5718
  • ls5718
  • 2016-04-23 15:52
  • 2852

二叉树的各种遍历实现伪代码

本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。作者:gfree.wind@gmail.com博客:linuxfocus.blog.chinaunix.net 最近...
  • sunjiangangok
  • sunjiangangok
  • 2017-04-10 10:31
  • 1533

二叉树的构建,遍历等基本操作

#include"stdio.h" #include"stdlib.h" #include"string.h" typedef struct BTNode {         char data; &...
  • lxslx
  • lxslx
  • 2016-09-16 08:53
  • 849

创建二叉树的两种方法以及三种遍历方法

二叉树的两种创建方法和三种遍历方法 这里的两种创建方法,一种值得是 数据结构上面的创建方法: 方法一 代码如下: 二叉树的结构定义如下: typedef struct BinaryTreeNode{ char value; struct ...
  • yujin753
  • yujin753
  • 2015-01-26 11:32
  • 8307

二叉树建立与遍历递归操作c++实现

#include #include #include using namespace std; //二叉树的数据类型 typedef char BiTType; //二叉树结构体 typedef struct BiTNode { BiTType dat...
  • lwy313722871
  • lwy313722871
  • 2013-12-14 00:50
  • 1586

算法-二叉树的三种遍历方式

二叉树是一种常见的数据结构,其遍历实现的算法也是编程中经常会遇到的问题。可能在实际的工作中并不会太多用到,但是这是一个程序员的基本的内功心法。
  • Richard_vi
  • Richard_vi
  • 2017-03-07 17:31
  • 233

C++类实现二叉树的构建和遍历

#include #include #include using namespace std; /*二叉树的结构体*/ typedef struct BTree { int val; struct BTree *left,*right; }BTree; /*二叉树的类,包含着操作二叉树...
  • u014453898
  • u014453898
  • 2017-02-06 17:03
  • 1000

C++数据结构--按层次遍历二叉树

1.按层次遍历二叉树图解    示例所用到的二叉树: 实现代码: #include #include using namespace std; template //节点类 class node ...
  • piniheaven
  • piniheaven
  • 2013-08-01 08:22
  • 2586
    个人资料
    • 访问:155589次
    • 积分:4929
    • 等级:
    • 排名:第6843名
    • 原创:329篇
    • 转载:0篇
    • 译文:0篇
    • 评论:80条
    博客专栏
    最新评论