二叉树遍历算法详解

在上一篇C语言实现二叉树中有提到对于二叉树的遍历,包括前序,中序和后续遍历,以及层次遍历

大家已经熟悉了二叉树的前中后序遍历过程,大部分都采用了递归的思想来实现

在leetcode算法练习题中,有这样三道题:

  • 前序遍历

在leetcode中,最终将输出结果输出到数组中保存,上一篇博客提到的二叉树前中后遍历均打印输出,为了直观比较,先通过递归的形式实现

  • 前序遍历的递归实现:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/void BinaryTreePrevOrder(struct TreeNode* root,int* ret,int* index)
{if (root == NULL){return;}ret[(*index)++]=root->val;//索引创建为指针,递归回溯过程中可以及时更新BinaryTreePrevOrder(root->left,ret,index);BinaryTreePrevOrder(root->right,ret,index);
}
/*** Note: The returned array must be malloced, assume caller calls free().*/
int* preorderTraversal(struct TreeNode* root, int* returnSize){int index=0;int* arr=(int*)malloc(sizeof(int)*100);memset(arr,0,sizeof(int)*100);BinaryTreePrevOrder(root,arr,&index);*returnSize=index;return arr;
}

通过代码分析发现:首先开辟存放节点数据的数组,数组大小此处给了固定大小100,可以根据二叉树节点个数进行设置

比如获取二叉树节点个数代码如下:

//获取节点个数
int getsize(struct TreeNode* root)
{if(root){return getsize(root->left)+getsize(root->right)+1;}return 0;
}

同时,还需要注意一点,在遍历过程中,为了将访问的节点保存在数组中,必须创建索引来顺序存放

但由于采用了递归进行遍历,对此在回溯的过程中,为了索引能够及时更新,对此,需要将索引设置为全局变量或指针变量

  • 中序遍历的递归实现

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
void BinaryInOrder(struct TreeNode* root,int* arr,int* index)
{if(root==NULL)return;BinaryInOrder(root->left,arr,index);arr[(*index)++]=root->val;BinaryInOrder(root->right,arr,index);
}
/*** Note: The returned array must be malloced, assume caller calls free().*/
int* inorderTraversal(struct TreeNode* root, int* returnSize){int indexsum=0;int* arr=(int*)malloc(sizeof(int)*100);memset(arr,0,sizeof(int)*100);BinaryInOrder(root,arr,&indexsum);*returnSize=indexsum;return arr;
}

中序遍历与前序遍历类似,不同之处在于,先遍历左子树,再访问根节点,保存数组中,再遍历右子树

  • 后序遍历的递归实现

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*/
void BinaryAfterOrder(struct TreeNode* root,int* arr,int* index)
{if(root==NULL)return;BinaryAfterOrder(root->left,arr,index);BinaryAfterOrder(root->right,arr,index);arr[(*index)++]=root->val;
}
/*** Note: The returned array must be malloced, assume caller calls free().*/
int* postorderTraversal(struct TreeNode* root, int* returnSize){int index=0;int* ret=(int*)malloc(sizeof(int)*100);memset(ret,0,sizeof(int)*100);BinaryAfterOrder(root,ret,&index);*returnSize=index;return ret;
}

后序遍历的不同之处在于,先遍历左子树,再遍历右子树,最后访问根节点,保存在数组中返回

以上三种方法均基于递归思想实现,代码简洁,但递归回溯的过程有一定的理解难度

对此,本博客基于一种先进后出的数据结构“栈”实现遍历的非递归算法

其中,所用到的有关“栈”的接口定义以及接口具体实现可以参照C语言实现栈和队列这篇博客

  • 前序遍历的非递归实现

算法思想:

前序遍历中,对于每一个节点,最先访问的是以此节点开始的最左路径

前序遍历中,对于每一个树,最先访问的右子树是在访问左路径时最后遇到的右子树

左边的节点访问:自上向下

右边的节点访问:自下向上

算法步骤:

1.访问每一个节点开始的最左路径,访问到的每一个节点入栈
2.最左路径访问完成之后,获取栈顶元素,继续访问以栈顶元素的右子树为根的子结构,继续执行第1步
3.结束条件:栈为空&&右子树为空

算法图解:

代码实现:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*///非递归
#include<stdio.h>
#include<stdlib.h>
// 支持动态增长的栈
typedef struct TreeNode* STDataType;
typedef struct Stack
{STDataType* _data;int _size;int _capacity; // 容量
}Stack;
//检查容量
void checkCapcity(Stack* ps)
{if (ps->_size == ps->_capacity){int newCap = ps->_capacity == 0 ? 1 : 2 * ps->_capacity;ps->_data = (STDataType*)realloc(ps->_data, sizeof(STDataType)*newCap);ps->_capacity = newCap;}
}
// 初始化栈
void StackInit(Stack* ps)
{if (ps == NULL)return;ps->_data = NULL;ps->_size = ps->_capacity = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{if (ps == NULL)return;checkCapcity(ps);ps->_data[ps->_size++] = data;
}
// 出栈
void StackPop(Stack* ps)
{if (ps == NULL || ps->_size == 0)return;ps->_size--;}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{return ps->_data[ps->_size - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{if (ps == NULL)return 0;return ps->_size;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{if (ps == NULL||ps->_size==0)return 1;elsereturn 0;
}
// 销毁栈
void StackDestroy(Stack* ps)
{free(ps->_data);ps->_size = 0;ps->_capacity = 0;
}//获取节点个数
int getsize(struct TreeNode* root)
{if(root){return getsize(root->left)+getsize(root->right)+1;}return 0;
}int* preorderTraversal(struct TreeNode* root, int* returnSize)
{Stack st;StackInit(&st);int sz=getsize(root);int* arr=(int*)malloc(sizeof(int)*sz);int idx=0;while(root||!StackEmpty(&st)){//1访问最左路径while(root){arr[idx++]=root->val;StackPush(&st,root);root=root->left;}//2获取栈顶元素,访问右子树root=StackTop(&st);StackPop(&st);root=root->right;}*returnSize=idx;return arr;
}
  • 中序遍历的非递归实现

算法步骤:

1,以节点开始,走最左路径,此路径上遇到的每一个节点首先入栈,但不能访问
2,获取栈顶元素,访问栈顶元素
3,获取栈顶元素的右子树,继续执行第一步
4,结束:栈为空&&右子树为空

算法图解:

代码实现:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*///非递归
#include<stdio.h>
#include<stdlib.h>
// 支持动态增长的栈
typedef struct TreeNode* STDataType;
typedef struct Stack
{STDataType* _data;int _size;int _capacity; // 容量
}Stack;
//检查容量
void checkCapcity(Stack* ps)
{if (ps->_size == ps->_capacity){int newCap = ps->_capacity == 0 ? 1 : 2 * ps->_capacity;ps->_data = (STDataType*)realloc(ps->_data, sizeof(STDataType)*newCap);ps->_capacity = newCap;}
}
// 初始化栈
void StackInit(Stack* ps)
{if (ps == NULL)return;ps->_data = NULL;ps->_size = ps->_capacity = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{if (ps == NULL)return;checkCapcity(ps);ps->_data[ps->_size++] = data;
}
// 出栈
void StackPop(Stack* ps)
{if (ps == NULL || ps->_size == 0)return;ps->_size--;}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{return ps->_data[ps->_size - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{if (ps == NULL)return 0;return ps->_size;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{if (ps == NULL||ps->_size==0)return 1;elsereturn 0;
}
// 销毁栈
void StackDestroy(Stack* ps)
{free(ps->_data);ps->_size = 0;ps->_capacity = 0;
}//获取节点个数
int getsize(struct TreeNode* root)
{if(root){return getsize(root->left)+getsize(root->right)+1;}return 0;
}int* inorderTraversal(struct TreeNode* root, int* returnSize)
{Stack st;StackInit(&st);int sz=getsize(root);int* arr=(int*)malloc(sizeof(int)*sz);int idx=0;while(root||!StackEmpty(&st)){//1最左路径while(root){//arr[idx++]=root->val;StackPush(&st,root);root=root->left;}//2获取栈顶元素,访问栈顶元素 访问右子树root=StackTop(&st);arr[idx++]=root->val;StackPop(&st);root=root->right;}*returnSize=idx;return arr;
}
  • 后序遍历的非递归实现

算法步骤:

1,以节点开始,走最左路径,遇到每一个节点入栈,但不访问
2,获取栈顶元素:
    当前栈顶元素是否可以访问:
        a,没有右子树:可以访问 -->执行第一步
        b,右子树访问完成:可以访问 -->执行第一步
        c,有右子树,但没有访问:不能访问当前元素-->首先访问右子树-->执行第一步

算法图解:

代码实现:

/*** Definition for a binary tree node.* struct TreeNode {*     int val;*     struct TreeNode *left;*     struct TreeNode *right;* };*///非递归
#include<stdio.h>
#include<stdlib.h>
// 支持动态增长的栈
typedef struct TreeNode* STDataType;
typedef struct Stack
{STDataType* _data;int _size;int _capacity; // 容量
}Stack;
//检查容量
void checkCapcity(Stack* ps)
{if (ps->_size == ps->_capacity){int newCap = ps->_capacity == 0 ? 1 : 2 * ps->_capacity;ps->_data = (STDataType*)realloc(ps->_data, sizeof(STDataType)*newCap);ps->_capacity = newCap;}
}
// 初始化栈
void StackInit(Stack* ps)
{if (ps == NULL)return;ps->_data = NULL;ps->_size = ps->_capacity = 0;
}
// 入栈
void StackPush(Stack* ps, STDataType data)
{if (ps == NULL)return;checkCapcity(ps);ps->_data[ps->_size++] = data;
}
// 出栈
void StackPop(Stack* ps)
{if (ps == NULL || ps->_size == 0)return;ps->_size--;}
// 获取栈顶元素
STDataType StackTop(Stack* ps)
{return ps->_data[ps->_size - 1];
}
// 获取栈中有效元素个数
int StackSize(Stack* ps)
{if (ps == NULL)return 0;return ps->_size;
}
// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0
int StackEmpty(Stack* ps)
{if (ps == NULL||ps->_size==0)return 1;elsereturn 0;
}
// 销毁栈
void StackDestroy(Stack* ps)
{free(ps->_data);ps->_size = 0;ps->_capacity = 0;
}//获取节点个数
int getsize(struct TreeNode* root)
{if(root){return getsize(root->left)+getsize(root->right)+1;}return 0;
}int* postorderTraversal(struct TreeNode* root, int* returnSize)
{Stack st;StackInit(&st);int sz=getsize(root);int* arr=(int*)malloc(sizeof(int)*sz);int idx=0;struct TreeNode* prev=NULL;//指向上一次访问节点while(root||!StackEmpty(&st)){//1最左路径,不访问while(root){//arr[idx++]=root->val;StackPush(&st,root);root=root->left;}//2通过指针获取栈顶元素struct TreeNode* top=StackTop(&st);//指向栈顶的指针//判断栈顶元素是否可以访问//可以访问,没有右子树,或右子树访问完成(上一次访问节点指向右子树根结点)if(top->right==NULL||top->right==prev){//可以访问当前栈顶元素arr[idx++]=top->val;//出栈StackPop(&st);//更新prevprev=top;}else{//右子树存在,且没有访问完成root=top->right;}}*returnSize=idx;return arr;
}
  • 执行结果:

二叉树遍历算法详解(递归法+非递归法)相关推荐

  1. 二叉树遍历算法的六种c语言实现 递归与非递归

    二叉树遍历分为三种: 先序遍历:先访问根结点,其次左节点,最后右节点 中序遍历:先访问左结点,其次跟节点,最后右节点 后序遍历:先访问左结点,其次右节点,最后根节点 三种遍历的递归算法实现形式类似,仅 ...

  2. 数据结构和算法详解(三)——递归、排序、散列表

    一.递归 一.什么是递归? 1.递归是一种非常高效.简洁的编码技巧,一种应用非常广泛的算法,比如DFS深度优先搜索.前中后序二叉树遍历等都是使用递归. 2.方法或函数调用自身的方式称为递归调用,调用称 ...

  3. 【算法详解】如何使用递归,递归使用的技巧详解

    什么是递归 先来看下百度百科的定义: 程序调用自身的编程技巧称为递归( recursion).递归作为一种算法在程序设计语言中广泛应用. 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法, ...

  4. 一文搞懂二叉树遍历---超详解(二叉树逐步剖析二)

    大家好!这里是小张,上次我们说到了二叉树的存储结构,今天我们继续来说说二叉树的遍历,废话不多说,我们现在就开始! 另外有很多小伙伴们在学习算法的时候,只去学习一些关于算法理论的知识,并不知道自己的代码 ...

  5. 非线性结构数据及遍历算法详解

    1.二叉树概念 指任意节点的度小于等于2的有序树 分满二叉树 普通二叉树 满二叉树:节点个数达到最大值2e(k)-1 K为层次 父亲节点: 根 左孩子节点 右孩子节点 度 :一个节点的孩子个数 层次: ...

  6. SF图像滤镜/美颜/美妆算法详解与实战

    本专栏将结合本热多年相关经验,从传统算法到火热的AI算法,给大家详细讲解目前在PC图像软件.手机图像处理类应用app,以及视频直播等应用类型中,图像视频的滤镜特效,人像美颜美妆特效的算法理论,并结合具 ...

  7. 张正友相机标定算法详解

    张正友相机标定算法详解 1.齐次表示法与一些基本结论 1.1 点与直线的齐次表示 ​ 在射影几何中,通常用齐次方式来表达点与直线.比如p=(u,v)p=(u,v)p=(u,v)被表示成p^=(x1,x ...

  8. KNN算法详解及实现

    KNN算法详解及实现 k近邻法(k-nearest neighbor,k-NN)是一种基本的分类和回归方法,是监督学习方法里的一种常用方法.k近邻算法假设给定一个训练数据集,其中的实例类别已定.分类时 ...

  9. 九十五、二叉树的递归和非递归的遍历算法模板

    @Author:Runsen 刷Leetcode,需要知道一定的算法模板,本次先总结下二叉树的递归和非递归的遍历算法模板. 二叉树的四种遍历方式,前中后加上层序遍历.对于二叉树的前中后层序遍历,每种遍 ...

最新文章

  1. 重置密码遇到ERROR 1045 (28000): Access denied for user ‘root‘@‘localhost‘ (using passwor:yes)问题
  2. C++ Primer 5th笔记(chap 15 OOP)继承概念
  3. [云炬ThinkPython阅读笔记]3.1 函数调用
  4. java 图片分割_Java atlas图集分割
  5. Android App列表之游标ListView(索引ListView)
  6. java中spring的级别_java – 在简单的示例项目中更改Spring框架日志级别?
  7. 无人驾驶遇见人工智能 百度将推有“大脑”的汽车
  8. 自己写的简易多任务系统---基于pic18fxxx
  9. kindeditor和easyui整合出不来
  10. 分页查询抽象出的对象属性(笔记)
  11. C语言数理逻辑题目,数学逻辑推理题整理,看看你能答对多少
  12. 2021巨量引擎手机行业人群洞察白皮书
  13. 机器学习实战9-运行Tensorflow(california_housing数据集)
  14. 6-7 使用函数输出水仙花数_自学C++基础教程(输入输出2)
  15. 如何让IOS应用从容地崩溃
  16. DesignSurface简介
  17. Linq To Sql进阶系列(三)CUD和Log
  18. 【C 语言】文件操作 ( fseek 函数 )
  19. 高一计算机算法教案,高中信息技术 算法及其实现 教案
  20. 谈谈机器视觉的那点事儿!

热门文章

  1. Spring Batch 写Excel数据
  2. 拉伯配资|战略新兴产业火了,高增长低估值股曝光
  3. [细碎的Java1.0]EDG我RNG 三巨头
  4. 创业注意细节更好注重骨架,快速实现产品目标
  5. 乔布斯经典语录精选:对产品、对手和生死感悟
  6. 机器学习-4.朴素贝叶斯分类器
  7. 统计扣分加分java_再也不怕违章扣分了,驾照也能够加分,最高+21分!快来看怎么加...
  8. MAC隐藏文件或文件夹命令
  9. 最好的管理:少讲道理,多打胜仗
  10. 华为解锁网站停止服务器,华为重要服务宣布终止!