C语言实现二叉树遍历的递归和非递归算法

本文主要介绍二叉树的各种遍历方法

二叉树的遍历

所谓二叉树的遍历,是指按某条搜索路径访问树中的每个结点,使得每个结点均被访问一次,而且仅被访问一次

由二叉树的递归定义可知,遍历一棵二叉树便要决定对根结点 N N N左子树 L L L右子树 R R R 的访问顺序。按照先遍历左子树再遍历右子树的原则,常见的遍历次序有:

  1. 前序遍历:(N L R)
  2. 中序遍历:(L N R)
  3. 后序遍历:(L R L)

这里的指的是根结点何时被访问

在这里插入图片描述

在介绍3种遍历算法前,我们先给出二叉树的存储结构和建立二叉树的代码

所有代码均来自c(c++)实现树(二叉树)的建立和遍历算法(一)(前序,中序,后序)

  1. 二叉树的存储结构(二叉链表)
//二叉树的二叉链表结构,也就是二叉树的存储结构,1个数据域,2个指针域(分别指向左右孩子)
typedef  struct BiTNode
{
    ElemType data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;
  1. 建立二叉树(此处以前序遍历的方式建立
//二叉树的建立,按前序遍历的方式建立二叉树,当然也可以以中序或后序的方式建立二叉树
void CreateBiTree(BiTree *T)
{
    ElemType ch;
    cin >> ch;
    if (ch == '#')
        *T = NULL;  //保证是叶结点
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        //if (!*T)
            //exit(OVERFLOW); //内存分配失败则退出。
        (*T)->data = ch;//生成结点
        CreateBiTree(&(*T)->lchild);//构造左子树
        CreateBiTree(&(*T)->rchild);//构造右子树    
    }
}

下面给出2种算法,分别实现三种遍历方式

递归算法

程序实现

/递归方式前序遍历二叉树
void PreOrderTraverse(BiTree T, int level)
{
    if (T == NULL)
        return;

/*此处表示对遍历的树结点进行的操作,根据你自己的要求进行操作,这里只是输出了结点的数据*/
    //operation1(T->data);
    operation2(T->data, level); //输出了层数

    PreOrderTraverse(T->lchild, level + 1);
    PreOrderTraverse(T->rchild, level + 1);
}

//递归方式中序遍历二叉树

void InOrderTraverse(BiTree T,int level)
{
	if(T==NULL)
		return;
	InOrderTraverse(T->lchild,level+1);

	//operation1(T->data);
	operation2(T->data, level); //输出了层数

	InOrderTraverse(T->rchild,level+1);
}

//递归方式后序遍历二叉树

void PostOrderTraverse(BiTree T,int level)
{
	if(T==NULL)
		return;
	PostOrderTraverse(T->lchild,level+1);
	PostOrderTraverse(T->rchild,level+1);

	//operation1(T->data);
	operation2(T->data, level); //输出了层数
}

下面是完整的实现代码

#include<iostream>
#include<stdlib.h>
using namespace std;

typedef char ElemType;

//二叉树的二叉链表结构,也就是二叉树的存储结构,1个数据域,2个指针域(分别指向左右孩子)

typedef  struct BiTNode
{
    ElemType data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

//二叉树的建立,按前序遍历的方式建立二叉树,当然也可以以中序或后序的方式建立二叉树
void CreateBiTree(BiTree *T)
{
    ElemType ch;
    cin >> ch;
    if (ch == '#')
        *T = NULL;  //保证是叶结点
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        //if (!*T)
            //exit(OVERFLOW); //内存分配失败则退出。
        (*T)->data = ch;//生成结点
        CreateBiTree(&(*T)->lchild);//构造左子树
        CreateBiTree(&(*T)->rchild);//构造右子树    
    }
}
//表示对遍历到的结点数据进行的处理操作,此处操作是将树结点前序遍历输出
void operation1(ElemType ch)
{
    cout << ch << " ";
}
//此处在输出的基础上,并输出层数
void operation2(ElemType ch, int level)
{
       cout << ch << "在第" << level << "层" << endl;
}


//递归方式前序遍历二叉树
void PreOrderTraverse(BiTree T, int level)
{
    if (T == NULL)
        return;
/*此处表示对遍历的树结点进行的操作,根据你自己的要求进行操作,这里只是输出了结点的数据*/
    //operation1(T->data);
    operation2(T->data, level); //输出了层数
    PreOrderTraverse(T->lchild, level + 1);
    PreOrderTraverse(T->rchild, level + 1);
}

//递归方式中序遍历二叉树

void InOrderTraverse(BiTree T,int level)
{
	if(T==NULL)
		return;
	InOrderTraverse(T->lchild,level+1);
	//operation1(T->data);
	operation2(T->data, level); //输出了层数
	InOrderTraverse(T->rchild,level+1);
}

//递归方式后序遍历二叉树

void PostOrderTraverse(BiTree T,int level)
{
	if(T==NULL)
		return;
	PostOrderTraverse(T->lchild,level+1);
	PostOrderTraverse(T->rchild,level+1);
	//operation1(T->data);
	operation2(T->data, level); //输出了层数
}


int main()
{
    int level = 1; //表示层数
    BiTree T = NULL;
    cout << "请以前序遍历的方式输入扩展二叉树:"; //类似输入AB#D##C##
    CreateBiTree(&T);// 建立二叉树,没有树,怎么遍历

    cout << "递归前序遍历输出为:" << endl;
    PreOrderTraverse(T, level);//进行前序遍历,其中operation1()和operation2()函数表示对遍历的结点数据进行的处理操作
    cout << endl;

    cout << "递归中序遍历输出为:" << endl;
    InOrderTraverse(T, level);
    cout << endl;

    cout << "递归后序遍历输出为:" << endl;
    PostOrderTraverse(T, level);
    cout << endl;

    return 0;
}

注意:

  1. 建立二叉树时,这里是以前序遍历的方式,输入的是扩展二叉树,也就是要告诉计算机什么是叶结点,否则将一直递归,当输入“#”时,指针指向NULL,说明是叶结点。

如下图为扩展二叉树:(前序遍历为:ABDG##H###CE#I##F##)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tDym9p2U-1583935067741)(http://liufan.vip/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/2019-4-6-%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%912/2.png)]

  1. operation1( )函数只是对各个结点的输出;
  2. operation2( )函数不仅输出了各个结点,同时输出了结点所在的层数。

运行结果

  1. 只是运行了operation2( )函数,有层数输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d1C9zEB6-1583935067742)(http://liufan.vip/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/2019-4-6-%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%912/3.png)]

  1. 只是运行了operation1( )函数,输出值:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aEqNAhKP-1583935067742)(http://liufan.vip/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/2019-4-6-%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B9%8B%E6%A0%912/4.png)]

非递归算法

  1. 我们可以借助栈实现3种遍历的非递归算法
  2. 除此之外,还给出了二叉树的层次遍历算法,所谓层次遍历,就是自顶向下、自左至右的遍历二叉树中的元素,可以借助队列实现

程序实现

//非递归方式前序遍历
/* 思路:将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。*/
void PreOrder(BiTree T){
    stack<BiTree> stack;
    //p是遍历指针  
    BiTree p = T;
    //p不为空或者栈不空时循环
    while (p || !stack.empty())
{
        if (p != NULL)
                 {
            //存入栈中  
            stack.push(p);
            //对树中的结点进行操作
            operation1(p->data);
             //遍历左子树  
            p = p->lchild;
         }
        else
                {
                 //退栈  
            p = stack.top();
            stack.pop();
            //访问右子树  
            p = p->rchild;
        }
    } 
}
//非递归中序遍历
void InOrder(BiTree T)
{
    stack<BiTree> stack;
    //p是遍历指针  
    BiTree p = T;
    //p不为空或者栈不空时循环  
    while (p || !stack.empty())
       {
        if (p != NULL)
                  {
            //存入栈中  
            stack.push(p);
            //遍历左子树  
            p = p->lchild;
         }
        else
                {
            //退栈
            p = stack.top();
            operation1(p->data); //对树中的结点进行操作
            stack.pop();
            //访问右子树  
            p = p->rchild;
        }
    } 
}
//非递归后序遍历
typedef struct BiTNodePost{
    BiTree biTree;
    char tag;
}BiTNodePost, *BiTreePost;

void PostOrder(BiTree T)
{
    stack<BiTreePost> stack;
    //p是遍历指针  
    BiTree p = T;
    BiTreePost BT;
    //栈不空或者p不空时循环  
    while (p != NULL || !stack.empty())
        {
        //遍历左子树  
        while (p != NULL)
               {
            BT = (BiTreePost)malloc(sizeof(BiTNodePost));
            BT->biTree = p;
            //访问过左子树  
            BT->tag = 'L';
            stack.push(BT);
            p = p->lchild;
        }
        //左右子树访问完毕访问根节点  
        while (!stack.empty() && (stack.top())->tag == 'R')
                {
            BT = stack.top();
            //退栈  
            stack.pop();
            p=BT->biTree;
            cout<<BT->biTree->data<<" ";
        }
        //遍历右子树  
        if (!stack.empty())
                {
            BT = stack.top();
            //访问过右子树  
            BT->tag = 'R';
            p = BT->biTree;
            p = p->rchild;
        }
    }
}
//层次遍历  
void LevelOrder(BiTree T)
{
    BiTree p = T; 
    queue<BiTree> queue;
    //根节点入队  
    queue.push(p);
    //队列不空循环  
    while (!queue.empty())
       {
        //对头元素出队  
        p = queue.front();
        //访问p指向的结点  
        operation1(p->data);
        //退出队列  
        queue.pop();
        //左孩子不为空,将左孩子入队  
        if (p->lchild != NULL)
                {
            queue.push(p->lchild);
        }
        //右孩子不空,将右孩子入队  
        if (p->rchild != NULL)
                {
            queue.push(p->rchild);
        }
    }
}

下面是完整的实现代码

#include<iostream>
#include<stdlib.h>
#include<stack>
#include<queue>
using namespace std;

typedef char ElemType;

//二叉树的二叉链表结构,也就是二叉树的存储结构,1个数据域,2个指针域(分别指向左右孩子)

typedef  struct BiTNode
{
    ElemType data;
    struct BiTNode *lchild, *rchild;
}BiTNode, *BiTree;

//二叉树的建立,按前序遍历的方式建立二叉树,当然也可以以中序或后序的方式建立二叉树
void CreateBiTree(BiTree *T)
{
    ElemType ch;
    cin >> ch;
    if (ch == '#')
        *T = NULL;  //保证是叶结点
    else
    {
        *T = (BiTree)malloc(sizeof(BiTNode));
        //if (!*T)
            //exit(OVERFLOW); //内存分配失败则退出。
        (*T)->data = ch;//生成结点
        CreateBiTree(&(*T)->lchild);//构造左子树
        CreateBiTree(&(*T)->rchild);//构造右子树    
    }
}

//表示对遍历到的结点数据进行的处理操作,此处操作是将树结点前序遍历输出
void operation1(ElemType ch)
{
    cout << ch << " ";
}
//此处在输出的基础上,并输出层数
void operation2(ElemType ch, int level)
{
       cout << ch << "在第" << level << "层" << "  ";
}


//递归方式前序遍历二叉树
void PreOrderTraverse(BiTree T, int level)
{
    if (T == NULL)
        return;
/*此处表示对遍历的树结点进行的操作,根据你自己的要求进行操作,这里只是输出了结点的数据*/
    operation1(T->data);
    //operation2(T->data, level); //输出了层数

    PreOrderTraverse(T->lchild, level + 1);
    PreOrderTraverse(T->rchild, level + 1);
}

//递归方式中序遍历二叉树

void InOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
InOrderTraverse(T->lchild,level+1);

operation1(T->data);
//operation2(T->data, level); //输出了层数

InOrderTraverse(T->rchild,level+1);
}

//递归方式后序遍历二叉树

void PostOrderTraverse(BiTree T,int level)
{
if(T==NULL)
return;
PostOrderTraverse(T->lchild,level+1);
PostOrderTraverse(T->rchild,level+1);

operation1(T->data);
//operation2(T->data, level); //输出了层数
}

//非递归方式前序遍历
/* 思路:将T入栈,遍历左子树;遍历完左子树返回时,栈顶元素应为T,出栈,再先序遍历T的右子树。*/
void PreOrder(BiTree T){
    stack<BiTree> stack;
    //p是遍历指针  
    BiTree p = T;
    //p不为空或者栈不空时循环
    while (p || !stack.empty())
{
        if (p != NULL)
                 {
            //存入栈中  
            stack.push(p);
            //对树中的结点进行操作
            operation1(p->data);
             //遍历左子树  
            p = p->lchild;
         }
        else
                {
                 //退栈  
            p = stack.top();
            stack.pop();
            //访问右子树  
            p = p->rchild;
        }
    } 
}
//非递归中序遍历
void InOrder(BiTree T)
{
    stack<BiTree> stack;
    //p是遍历指针  
    BiTree p = T;
    //p不为空或者栈不空时循环  
    while (p || !stack.empty())
       {
        if (p != NULL)
                  {
            //存入栈中  
            stack.push(p);
            //遍历左子树  
            p = p->lchild;
         }
        else
                {
            //退栈
            p = stack.top();
            operation1(p->data); //对树中的结点进行操作
            stack.pop();
            //访问右子树  
            p = p->rchild;
        }
    } 
}
//非递归后序遍历
typedef struct BiTNodePost{
    BiTree biTree;
    char tag;
}BiTNodePost, *BiTreePost;

void PostOrder(BiTree T)
{
    stack<BiTreePost> stack;
    //p是遍历指针  
    BiTree p = T;
    BiTreePost BT;
    //栈不空或者p不空时循环  
    while (p != NULL || !stack.empty())
        {
        //遍历左子树  
        while (p != NULL)
               {
            BT = (BiTreePost)malloc(sizeof(BiTNodePost));
            BT->biTree = p;
            //访问过左子树  
            BT->tag = 'L';
            stack.push(BT);
            p = p->lchild;
        }
        //左右子树访问完毕访问根节点  
        while (!stack.empty() && (stack.top())->tag == 'R')
                {
            BT = stack.top();
            //退栈  
            stack.pop();
            p=BT->biTree;
            cout<<BT->biTree->data<<" ";
        }
        //遍历右子树  
        if (!stack.empty())
                {
            BT = stack.top();
            //访问过右子树  
            BT->tag = 'R';
            p = BT->biTree;
            p = p->rchild;
        }
    }
}
//层次遍历  
void LevelOrder(BiTree T)
{
    BiTree p = T; 
    queue<BiTree> queue;
    //根节点入队  
    queue.push(p);
    //队列不空循环  
    while (!queue.empty())
       {
        //对头元素出队  
        p = queue.front();
        //访问p指向的结点  
        operation1(p->data);
        //退出队列  
        queue.pop();
        //左孩子不为空,将左孩子入队  
        if (p->lchild != NULL)
                {
            queue.push(p->lchild);
        }
        //右孩子不空,将右孩子入队  
        if (p->rchild != NULL)
                {
            queue.push(p->rchild);
        }
    }
}
int main()
{
    int level = 1; //表层数
    BiTree T = NULL;
    cout << "请以前序遍历的方式输入扩展二叉树:"; //类似输入AB#D##C##
    CreateBiTree(&T);// 建立二叉树,没有树,怎么遍历
    cout << "递归前序遍历输出为:" << endl;
    PreOrderTraverse(T, level);//进行前序遍历,其中operation1()和operation2()函数表示对遍历的结点数据进行的处理操作
    cout << endl;
    cout << "递归中序遍历输出为:" << endl;
    InOrderTraverse(T, level);
    cout << endl;
    cout << "递归后序遍历输出为:" << endl;
    PostOrderTraverse(T, level);
    cout << endl;
        cout<<"非递归前序遍历输出为:"<<endl;
        PreOrder(T);
        cout<<endl;
        cout<<"非递归前序遍历输出为:"<<endl;
        InOrder(T);
        cout<<endl;
        cout<<"非递归前序遍历输出为:"<<endl;
        PostOrder(T);
        cout<<endl;
        cout<<"层序遍历输出为:"<<endl;
        LevelOrder(T);
        cout<<endl;
    return 0;
}

运行结果

只是运行了operation1( )函数,输出值:


补充另外一种实现二叉树后序遍历的非递归算法

void PostOrder(BiTree T){
    InitStack(s);
    p=T;
    r=NULL;                                  //辅助判断右子树是否被访问过
    while(p||!IsEmpty(s)){
        if(p){                               //走到最左边
            push(s,p);
            p=p->lchild;
        }
        else{                                //向右
            GetTop(s,p);                     //取栈顶结点
            if(p->rchild&&p->rchild!=r){     //如果右子树存在且未被访问过
                p=p->rchild;                 //转向右
                push(s,p);                   //压入栈
                p=p->lchild;                 //再转向左
            }
            else{                            //否则,弹出结点并访问
                pop(s,p);                    //结点出栈
                visit(p->data);              //访问该结点
                r=p;                         //记录最近访问过的结点
                p=NULL;                      //结点访问完后,重置p指针
            }
        }
    }
}

重要结论

  1. 二叉树的前序和中序序列能唯一确定一棵二叉树
  2. 二叉树的后序和中序序列能唯一确定一棵二叉树
  3. 二叉树的层次和中序序列能唯一确定一棵二叉树

参考资料

c实现树(二叉树)的建立和遍历算法(一)(前序,中序,后序)
树(二叉树)的建立和遍历算法(二)

  • 49
    点赞
  • 310
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
二叉树的中序遍历可以通过非递归的方式实现,常用的方法是利用栈。具体的实现方法如下: 1. 初始化一个栈,将节点入栈。 2. 循环执行以下步骤,直到栈为空: 1. 将栈顶节点弹出,并将其右子节点(如果有)压入栈中。 2. 将当前节点压入栈中,并将其左子节点(如果有)置为当前节点。 3. 遍历完成。 下面是具体的代码实现: ```c #include <stdio.h> #include <stdlib.h> // 二叉树节点结构体 typedef struct TreeNode { int val; struct TreeNode* left; struct TreeNode* right; } TreeNode; // 栈节点结构体 typedef struct StackNode { TreeNode* node; struct StackNode* next; } StackNode; // 初始化栈 StackNode* initStack() { return NULL; } // 入栈 StackNode* push(StackNode* stack, TreeNode* node) { StackNode* new_node = (StackNode*)malloc(sizeof(StackNode)); new_node->node = node; new_node->next = stack; return new_node; } // 出栈 StackNode* pop(StackNode* stack) { if (stack == NULL) { return NULL; } StackNode* top = stack; stack = stack->next; free(top); return stack; } // 获取栈顶节点 TreeNode* top(StackNode* stack) { if (stack == NULL) { return NULL; } return stack->node; } // 中序遍历二叉树非递归) void inorderTraversal(TreeNode* root) { StackNode* stack = initStack(); // 初始化栈 TreeNode* cur = root; // 当前节点 while (stack != NULL || cur != NULL) { if (cur != NULL) { stack = push(stack, cur); // 当前节点入栈 cur = cur->left; // 左子节点置为当前节点 } else { cur = top(stack); // 获取栈顶节点 stack = pop(stack); // 栈顶节点出栈 printf("%d ", cur->val); // 打印节点值 cur = cur->right; // 右子节点置为当前节点 } } } int main() { // 构造二叉树 TreeNode* root = (TreeNode*)malloc(sizeof(TreeNode)); root->val = 1; root->left = (TreeNode*)malloc(sizeof(TreeNode)); root->left->val = 2; root->left->left = NULL; root->left->right = NULL; root->right = (TreeNode*)malloc(sizeof(TreeNode)); root->right->val = 3; root->right->left = (TreeNode*)malloc(sizeof(TreeNode)); root->right->left->val = 4; root->right->left->left = NULL; root->right->left->right = NULL; root->right->right = (TreeNode*)malloc(sizeof(TreeNode)); root->right->right->val = 5; root->right->right->left = NULL; root->right->right->right = NULL; // 中序遍历二叉树 inorderTraversal(root); printf("\n"); return 0; } ``` 上述代码中,`push`、`pop` 和 `top` 操作均是常规的栈操作,不再赘述。在 `inorderTraversal` 函数中,每次循环都将当前节点的左子节点置为当前节点,并将当前节点入栈,直到当前节点为空。然后,取出栈顶节点,打印节点值,并将右子节点置为当前节点。循环执行这个过程,直到栈为空。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

bluesliuf

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值