遍历

先序遍历


从树根开始绕着整棵树的外围转一圈,经过结点的顺序就是先序遍历的顺序
先序遍历结果:ABDHIEJCFKG

来源
来源

中序遍历

中序遍历可以想象成,按树画好的左右位置投影下来就可以了
中序遍历结果:HDIBEJAFKCG

B站来源

后序遍历

后序遍历就像是剪葡萄,我们要把一串葡萄剪成一颗一颗的。
还记得我们先序遍历绕圈的路线么?
就是围着树的外围绕一圈,如果发现一剪刀就能剪下的葡萄(必须是一颗葡萄),就把它剪下来,组成的就是后序遍历了。
后序遍历结果:HIDJEBKFGCA

让我们来看下动画

层序遍历

层序遍历太简单了,就是按照一层一层的顺序,从左到右写下来就行了。
后序遍历结果:ABCDEFGHIJK

算法思想
用一个队列保存被访问的当前节点的左右孩子以实现层次遍历

在进行层次遍历的时候,设置一个队列结构,遍历从二叉树的根节点开始,首先将根节点指针入队列,然后从队头取出一个元素,每取一个元素,执行下面两个操作:

  • 访问该元素所指向的节点
  • 若该元素所指节点的左右孩子节点非空,则将该元素所指节点的左孩子指针和右孩子指针顺序入队。此过程不断进行,当队列为空时,二叉树的层次遍历结束。

图:


二叉树的深度优先遍历DFS和广度优先遍历BFS:

  • DFS深度优先遍历:

    • 从根节点出发,沿着左子树进行纵向遍历,知道找到叶子节点为止。然后回溯到前一个节点,进行右子树节点的遍历,知道遍历完所有可达节点为止
    • 利用数据结构“栈”,父节点入栈,父节点出栈,先右子节点入栈,后左子节点入栈。递归遍历全部节点。
    • 前序遍历、中序遍历、后续遍历
  • BFS广度优先遍历:

    • 从根节点出发,在横向遍历二叉树层段节点的基础上纵向遍历二叉树的层次。
    • 利用数据结构“队列”,父节点入队,父节点出队列,先左子节点入队,后右子节点入队。递归遍历全部节点。
    • 层次遍历

深入理解三种遍历

来,让我们先把所有空结点都补上。
还记得我们先序和后序遍历时候跑的顺序么?按照这个顺序再跑一次,就是围着树的外围跑一整圈。

让我们来理解一下绕着外围跑一整圈的真正含义是:遍历所有结点时,都先往左孩子走,再往右孩子走。
观察一下,你有什么发现?
有没有发现,除了根结点和空结点,其他所有结点都有三个箭头指向它。
一个是从它的父节点指向它,一个是从它的左孩子指向它,一个是从它的右孩子指向它。
一个结点有三个箭头指向它,说明每个结点都被经过了三遍。一遍是从它的父节点来的时候,一遍是从它的左孩子返回时,一遍是从它的右孩子返回时。

其实我们在用递归算法实现二叉树的遍历的时候,不管是先序中序还是后序,程序都是按照上面那个顺序跑遍所有结点的。

先序中序和后序唯一的不同就是,在经过结点的三次中,哪次访问(输出或者打印或者做其他操作)了这个结点。有点像大禹治水三过家门,他会选择一次进去。

先序遍历顾名思义,就是在第一次经过这个结点的时候访问了它。就是从父节点来的这个箭头的时候,访问了它。
中序遍历也和名字一样,就是在第二次经过这个结点的时候访问了它。就是从左孩子返回的这个箭头的时候,访问了它。
后序遍历,就是在第三次经过这个结点的时候访问了它。就是从右孩子返回的这个箭头的时候,访问了它。

其实不管是前序中序还是后序,在程序里跑的时候都是按照同样的顺序跑的,每个结点经过三遍,第几遍访问这个结点了,就叫什么序遍历。

下面做一个实例吧

代码实现加以理解

基础实例图(下面代码均围绕此展开)

我们先用代码基本实现一下遍历

void TraverseBiTree(BiTree T)
{if (T == NULL)return ;TraverseBiTree(T->lChild);TraverseBiTree(T->rChlid);
}

这个递归就能实现当遇到空指针时候返回,记住,每个结点先往左孩子走,再往右孩子走这个顺序。

如果我们想实现先序遍历,只需要在第一次经过这个结点的时候访问(输出)他就可以了,只需要加上一句printf。

//先序遍历二叉树
void TraverseBiTree(BiTree T)
{if (T == NULL)return ;printf("%c ", T->data);TraverseBiTree(T->lChild);TraverseBiTree(T->rChlid);
}

中序遍历,就是在第二次经过这个结点的时候访问它。

//中序遍历二叉树
void InOrderBiTree(BiTree T)
{if (T == NULL)return ;InOrderBiTree(T->lChild);printf("%c ", T->data);InOrderBiTree(T->rChlid);
}

后序遍历,就是在第三次经过这个结点的时候访问它。

//后序遍历二叉树
void PostOrderBiTree(BiTree T)
{if (T == NULL)return ;PostOrderBiTree(T->lChild);PostOrderBiTree(T->rChlid);printf("%c ", T->data);
}

我们可以看到,差别仅仅是printf出现的位置,也就是我们访问结点的位置,在递归算法中其他并没有差别。

前序遍历的递推公式:
preOrder(r)=printr−>preOrder(r−>left)−>preOrder(r−>right)preOrder(r) = print r->preOrder(r->left)->preOrder(r->right)preOrder(r)=printr−>preOrder(r−>left)−>preOrder(r−>right)

中序遍历的递推公式:
inOrder(r)=inOrder(r−>left)−>printr−>inOrder(r−>right)inOrder(r) = inOrder(r->left)->print r->inOrder(r->right)inOrder(r)=inOrder(r−>left)−>printr−>inOrder(r−>right)
后序遍历的递推公式:
postOrder(r)=postOrder(r−>left)−>postOrder(r−>right)−>printrpostOrder(r) = postOrder(r->left)->postOrder(r->right)->print rpostOrder(r)=postOrder(r−>left)−>postOrder(r−>right)−>printr

二叉树遍历的时间复杂度:每个节点最多会被访问两次,所以遍历操作的时间复杂度,跟节点的个数 n 成正比,也就是说二叉树遍历的时间复杂度是O(n)。

c语言实现

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>typedef char  ElemType; //数据类型//定义二叉树结构
typedef struct BiTreeNode
{ElemType  data; //数据域struct BiTreeNode *lChild;struct BiTreeNode *rChlid;
} BiTreeNode, *BiTree;//先序创建二叉树
void CreateBiTree(BiTree *T)//要改变指针,所以要把指针的地址传进来
{ElemType ch;scanf("%c", &ch);//注意数据类型getchar();//吸收空格或者回车if (ch == '#')*T = NULL;else{*T = (BiTree)malloc(sizeof(BiTreeNode));if (!(*T))//检查是否分配成功exit(-1);(*T)->data = ch;CreateBiTree(&(*T)->lChild);//printf("输入%d的左孩子:", ch);CreateBiTree(&(*T)->rChlid);//printf("输入%d的右孩子:", ch);}
}//先序遍历二叉树
void TraverseBiTree(BiTree T)
{if (T == NULL)return ;printf("%c ", T->data);TraverseBiTree(T->lChild);TraverseBiTree(T->rChlid);
}//中序遍历二叉树
void InOrderBiTree(BiTree T)
{if (T == NULL)return ;InOrderBiTree(T->lChild);printf("%c ", T->data);InOrderBiTree(T->rChlid);
}//后序遍历二叉树
void PostOrderBiTree(BiTree T)
{if (T == NULL)return ;PostOrderBiTree(T->lChild);PostOrderBiTree(T->rChlid);printf("%c ", T->data);
}//主函数
int main(void)
{BiTree T;printf("请输入先序遍历顺序下各个结点的值,#表示没有结点:\n");CreateBiTree(&T);printf("先序遍历二叉树:\n");TraverseBiTree(T);printf("\n");printf("中序遍历二叉树:\n");InOrderBiTree(T);printf("\n");printf("后序遍历二叉树:\n");PostOrderBiTree(T);printf("\n");return 0;
}

c++实现

#include<iostream>using namespace std;typedef char  ElemType; //数据类型typedef struct BiTreeNode//定义结构体
{ElemType  data; //数据域struct BiTreeNode *lChild;//左孩子struct BiTreeNode *rChlid;//右孩子
} BiTreeNode, *BiTree;//先序创建二叉树
void CreateBiTree(BiTree &T)//要改变指针,C++可以把指针的引用传进来
{ElemType ch;cin >> ch;if (ch == '#')T = NULL;else{T = new BiTreeNode;T->data = ch;CreateBiTree(T->lChild);//cout<<"输入"<<ch<<"的左孩子:" ;CreateBiTree(T->rChlid);//cout<<"输入"<<ch<<"的右孩子:" ;}
}//先序遍历二叉树
void TraverseBiTree(BiTree T)
{if (T == NULL)return ;cout << T->data <<" ";TraverseBiTree(T->lChild);TraverseBiTree(T->rChlid);
}//中序遍历二叉树
void InOrderBiTree(BiTree T)
{if (T == NULL)return ;InOrderBiTree(T->lChild);cout << T->data <<" ";InOrderBiTree(T->rChlid);
}//后序遍历二叉树
void PostOrderBiTree(BiTree T)
{if (T == NULL)return ;PostOrderBiTree(T->lChild);PostOrderBiTree(T->rChlid);cout << T->data <<" ";
}int main(void)
{BiTree T;cout << "请输入先序遍历顺序下各个结点的值,#表示没有结点:" << endl;CreateBiTree(T);cout<<"先序遍历二叉树:"<<endl;TraverseBiTree(T);cout<<endl;cout<<"中序遍历二叉树:"<<endl;InOrderBiTree(T);cout<<endl;cout<<"后序遍历二叉树:"<<endl;PostOrderBiTree(T);cout<<endl;return 0;
}

题目

1、已知一颗二叉树的前序遍历序列为ABCDEF,中序遍历为CBAEDF,请问这棵二叉树的后续遍历是?

  • 三种遍历都是从根节点开始的:

    • 前序遍历的结果是ABCDEF,所以根节点是A
    • 中序遍历的结果为CBAEDF,所以得出了A的左右节点:
  • 因为前序遍历ABCDEF, 先打印B(A的后面就马上打印了B),所以B一定是A的左孩子,而C只能是B的孩子。
  • 因为中序遍历CBAEDF, C在B前面打印,所以C是B的左孩子
  • 因为前序遍历ABCDEF。所以D是A的右孩子。EF是D的子孙。(注意,它们中有一个不一定是孩子,有可能是孙子
  • 因为中序遍历CBAEDF, 由于E在D的左侧,F在右侧。所以可以确定E是D的左孩子,F是D的右孩子。

    所以后序遍历为:CBEFDA

2、二叉树的中序遍历是ABCDEFG,后序遍历是BDCAFGE,求前序遍历。

  • 因为后续遍历是BDCAFGE,所以E是根节点。前序遍历的首字母是E
  • 于是中序遍历分为两棵树ABCD和FG。接下来判断E的右孩子是F还是G
  • 中序遍历FG,因为是打印左孩子—>自己---->右孩子,所以F是G的左孩子。
    * 对于中序遍历ABCDEFG,与后序遍历BDCAFGE
  • 对于中序遍历ABCDEFG,因为后序遍历BDCAFGE,

秒懂二叉树中的前序、中序,后序遍历元素排列顺序

动画:二叉树遍历的多种姿势
动画 | 什么是二分搜索树(二叉查找树)
java实现树的一般操作

算法:图解二叉树的遍历相关推荐

  1. 深度优先遍历算法-03二叉树路径遍历问题

    二叉树路径遍历 简述 比较基础的一个DFS的题目,但是确实很多难题的模板.LeetCode很多二叉树的题本质上就是这个路径遍历. 本题为了输出路径,使用DFS的经典结构栈完成. 问题描述 给定一个二叉 ...

  2. 算法(2)-二叉树的遍历(递归/迭代)python实现

    二叉树的遍历 1.深度优先DFS 1.1 DFS 递归解法 1.1.1先序遍历 1.1.2中序遍历 1.1.3后序遍历 1.2 DFS迭代解法 1.2.1先序遍历 1.2.2中序遍历 1.2.3后序遍 ...

  3. 二叉树层次遍历算法 python_二叉树的遍历详解:前、中、后、层次遍历(Python实现)...

    二叉树的遍历详解:前.中.后.层次遍历(Python实现) 二叉树是一种常见的数据结构,而它的常见遍历方法有前序遍历.中序遍历.后续遍历.层次遍历--掌握这几种遍历方法是很有必要的. 假设我们二叉树节 ...

  4. 数据结构与算法之二叉树广度遍历、深度遍历总结

    什么是树,它是和链表一样都是一种抽象数据类型(ADT),包括数据结构和对数据的操作. 树是一种二维平面的数据结构结构,它也是由节点组成的,只是它的后继节点不止一个,而链表的后继节点只有一个. 树具有以 ...

  5. 【数据结构与算法】二叉树深度遍历

    leetcode:力扣本题链接 leetcode所有深度遍历链接 !!!后文有动画演示一定要结合动画理解!!! 遍历顺序取决于中间节点所在的位置,假如中间节点在最后,那么就是后序遍历. 在非递归算法中 ...

  6. 图解二叉树非递归版的前序遍历算法

    " 图解用栈数据结构对树的前序遍历,中序遍历,后续遍历." 树的遍历 所谓遍历 (Traversal) 是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问.访问结点所做 ...

  7. 讲透学烂二叉树(三):二叉树的遍历图解算法步骤及JS代码

    二叉树的遍历是指不重复地访问二叉树中所有结点,主要指非空二叉树,对于空二叉树则结束返回. 二叉树的遍历分为 深度优先遍历 先序遍历:根节点->左子树->右子树(根左右),有的叫:前序遍历 ...

  8. 图解二叉树非递归版的中序遍历算法

    你会学到什么 讨论的问题是什么 这个问题相关的概念和理论 非递归版中序遍历算法 代码思考 算法技巧 实现代码 快照 评价算法 总结 欢迎关注算法思考与应用公众号 你会学到什么? 树的递归遍历算法很容易 ...

  9. 【数据结构与算法基础】二叉树与其遍历序列的互化 附代码实现(C和java)

    前言 数据结构,一门数据处理的艺术,精巧的结构在一个又一个算法下发挥着他们无与伦比的高效和精密之美,在为信息技术打下坚实地基的同时,也令无数开发者和探索者为之着迷. 也因如此,它作为博主大二上学期最重 ...

最新文章

  1. jQuery 在Table中选择input之类的东西注意事项
  2. 口语学习Day2:今天来说说我的小客厅!
  3. 使用jquery的blockui插件显示弹出层
  4. html5编程技术代码,编程技术—CSS技术
  5. python可以做力扣的题吗_力扣周赛 198 - python 解答
  6. 无法卸载 Mac 上的磁盘时该怎么办?
  7. 最新BIOS设置中英文对照表
  8. vbs无限循环代码_vbs整人代码
  9. wps页码从指定页开始_wps怎么自动生成页码以及设置页码从第三页开始
  10. Permute3 mac最新多种媒体视频格式转换工具
  11. cass有坐标文件生成里程文件_南方CASS里程文件生成
  12. 镭神C16上位机软件显示
  13. 2021年保育员(中级)考试及保育员(中级)考试总结
  14. Python Png转格式为Pdf。
  15. 激光雷达学习笔记(一)数据采集
  16. Java程序员的技术进阶成长路线
  17. cannot simultaneously fetch multiple bags
  18. 拼多多贴钱卖车,揭示汽车经销商现状
  19. 408计算机网络学习笔记——计算机网络体系结构
  20. Python-charle+fiddler

热门文章

  1. 手游换皮发海外,如果规避游戏侵权风险?
  2. 小程序预览轮播图 大图
  3. MATLAB使用readtable读取首行变量名失败
  4. systemtap PHP,systemtap 进阶
  5. 【调剂】沈阳化工大学2023年研究生调剂专业及咨询电话
  6. android自定义弧形,Android 自定义弧形旋转菜单栏——卫星菜单
  7. thinkphp3.1.3 getshell_ThinkPHP3由注入导致的getshell
  8. 全球首个完全开源的指令跟随大模型;T5到GPT-4最全盘点
  9. OSChina 周五乱弹 —— 我就和你们不一样了,我是长得很搞笑
  10. Tableau百分比完成进度条制作