本篇会加入个人的所谓鱼式疯言
❤️❤️❤️鱼式疯言
:❤️❤️❤️此疯言非彼疯言
而是理解过并总结出来通俗易懂的大白话,
小编会尽可能的在每个概念后插入鱼式疯言
,帮助大家理解的.
🤭🤭🤭可能说的不是那么严谨
.但小编初心是能让更多人能接受我们这个概念
!!!
前言
在上篇中我们学习了 二叉树的基本概念
以及他们的特性结论,并运用到了 具体的题目 中去解决问题 。
而在本篇中,小编讲继续学习 二叉树
的基本操作, 主要围绕着我们 遍历二叉树 来讲解 , 人狠话不多,下面让我们切入主题吧 💥 💥 💥
目录
-
二叉树的遍历初识
-
前序遍历
-
中序遍历
4.后序遍历
-
层序遍历
-
二叉树遍历的应用
一. 二叉树的遍历初识
学习二叉树的结构,最简单的方式就是遍历,所谓遍历 是指 沿着某条搜索路线,依次树中的某个节点均做一次访问, 访问节点所做的操作 依赖于要解决的各种实际问题。
遍历是二叉树是最重要的操作之一,是 二叉树上进行其他运算
的基础
1. 二叉树的遍历简介
在遍历二叉树时, 如果没有进行某种约定,每个人都按照自己的方式来遍历, 得到的结果就比较乱, 如果我们按照某个规则 来遍历, 则每个人对于遍历结果都是相同的 , 如果 N 代表 根节点
,L 代表左节点
, R 代表 右节点
, 那根据遍历的的节点有以下的遍历方式。
-
NLR: 前序遍历 (先序遍历) 根据
根——》 左 ——》 右
的顺序对二叉树进行遍历 -
LNR : ==中序遍历 ==: 根据
左——》 根——》 右
的顺序 对二叉树进行遍历 -
LRN 后序遍历 : 根据
左——》 右 ——》 根
的顺序对二叉树进行遍历
详细的遍历方式, 小编下面细讲哦 💖 💖 💖 💖
在遍历二叉树之前, 我们先用一下代码简单的 构建一颗二叉树
public class MyBinaryTree {public static class TreeNode {public TreeNode left;public TreeNode right;public char val;public TreeNode(char val) {this.val = val;}}private TreeNode root;// 构造二叉树public TreeNode createBinaryTree() {root=new TreeNode('A');TreeNode B=new TreeNode('B');TreeNode D=new TreeNode('D');TreeNode E=new TreeNode('E');TreeNode H=new TreeNode('H');TreeNode C=new TreeNode('C');TreeNode F=new TreeNode('F');TreeNode G=new TreeNode('G');root.left=B;B.left=D;B.right=E;E.right=H;root.right=C;C.left=F;C.right=G;return root;}// 前序遍历
void preOrder(Node root);
// 中序遍历
void inOrder(Node root);
// 后序遍历
void postOrder(Node root);}
二. 前序遍历
1. 前序遍历的特点
按照从左子树开始走,一直 往下递归,每一步所走的路径成为我们的根,先遍历完根之后。
按照根左右的顺序, 当我们走完每个根节点的左子树 时, 先往下递, 再往
回归
, 左节点成为新的根, 会到最初的根节点之后,再向右子树进行先递后归
的操作,
动画演示
2. 前序遍历的实现
因为前序遍历有 递归 和 非递归
的两种方式, 但 遍历的原理和方向都是一致的
在本篇文章中,。小编都会带着小伙伴们 一 一 实现 💥 💥 💥 💥
<1>. 前序遍历的递归实现
// 前序遍历public void FirstDisplay(TreeNode root) {if (root==null) {return;}System.out.print(root.val+" ");FirstDisplay(root.left);FirstDisplay(root.right);}
这里的代码的递归思路就是完美的按照我们遍历方向来的,
先访问,后递归
<2>. 前序遍历的非递归实现
// 非递归的前序遍历public void FirstDisplayNo(TreeNode root) {// 先创建一个栈来存放树的每个节点Stack<TreeNode> stack=new Stack<>();// 先把艮节点创建一遍TreeNode cur=root;/*** 外循环主要遍历 右边的节点* 用于出栈的数据* 并让节点向右移动*/while (cur != null || !stack.empty()) {/*** 在这个内循环中* 当往左走就添加数据,一直到为 null 结束* 并进行打印*/while (cur != null) {// 先打印System.out.print(cur.val+" ");// 打印完就入栈stack.add(cur);// 节点向左移动cur=cur.left;}// 出栈存放数据cur=stack.pop();// 并向右走cur=cur.right;// 当再次循环时,如果左边还有节点就会继续存放}System.out.println();}
非递归的 实现步骤
- 先定义一个栈 , 来记录我们每次遍历过的
根节点
- 先让根节点一直
向左走
,当遍历完我们的 左子树 (也就是我们的root = null
时候), 并且入栈, 记录下来以便后面我们遍历右子树
- 然后出栈, 开始
向右走
, 遍历我们的右子树
- 当整个栈为
null
并且到达的这个节点 cur 也为null
, 就意味着遍历完整个 二叉树所有的节点
鱼式疯言
无论是 递归
还是非递归
的 前序遍历 , 我们的 前序遍历思路
就是
先走根 , 根走完走左 , 左走完回到根,
再走右
,一层一层的走,一步一步的回
。
细节处理:
在代码上我们要注意的就是这个当节点为 null ,也就意味着我们要开始 回退 到 上一个节点
二. 中序遍历
1. 中序遍历的特点
我们知道 中序遍历 , 是以 左- 根-右的顺序 进行遍历
我们先从 走左边, 还是让每个左节点先成为新的根, 当这个新的根的
左子树
都走完之后, 才能真正访问我们当前 新的节点。
以此类推,我们新的节点访问结束后,就会进行回退到前一个旧的节点,继续访问,最终当整个 左子树走完 , 并且 访问完我们的根 , 就遍历我们的
右子树
,最终回到我们整颗树的根节点
动画演示
2.中序遍历的实现
<1>.中序遍历的递归实现
// 中序遍历
public void middleDisplay(TreeNode root) {if (root==null) {return;}middleDisplay(root.left);System.out.print(root.val+" ");middleDisplay(root.right);
}
这里的代码的递归思路就是完美的按照我们遍历方向来的,
先递归,后访问
,小编在这里就 不赘述 了
<2>. 中序遍历的非递归实现
// 非递归的中序遍历public void middleDisplayNo (TreeNode root) {// 创建一个栈用于回退节点Stack<TreeNode> stack=new Stack<>();// 先放根节点TreeNode cur=root;/*** 外循环主要用于遍历 右边* 更是用于出栈的回退*/while (cur != null || !stack.empty()) {/*** 内循环先遍历下去* 边遍历边存放*/while (cur != null) {stack.add(cur);cur=cur.left;}// 出栈最后一个无左节点的左子树cur=stack.pop();// 打印该节点System.out.print(cur.val+" ");// 再往右走cur=cur.right;}System.out.println();}
非递归的实现步骤:
我们先定义一个 栈
,用来存储走过的每个 左子树的节点
-
先
往左边
的节点走,先整个左子树
的每个节点都入栈, 当 这个节点 为 null 就停止入栈
-
然后进行出栈, 出栈的时候,我们就可以对该节点进行打印(访问) , 并且向
右子树节点
开始走 -
当整个栈为
null
并且 该节点也为 null , 也就意味着遍历完二叉树所有的节点
鱼式疯言
中序遍历的最核心的要点就是
无论是
递归
还是非递归
的 中序遍历:
一定要先
走完每个左子树
, 当我们进行 回退 的时候。 才轮的到该 根节点去遍历, 最后才走右子树
的一种 顺序.
三. 后序遍历
1. 后序遍历的特点
后序遍历的顺序就是 : 左-右-根
的顺序,
还是先走左边的节点,让 左边的节点 成为 新的根 , 直到找到走完整个
左子树
,回退后继续走 右子树,当 右子树走完之后,回去的根节点就是我们要访问
的
动画演示
2. 后序遍历的实现
<1>. 后序遍历的递归实现
// 后序遍历public void lastDisplay(TreeNode root) {if (root==null) {return;}lastDisplay(root.left);lastDisplay(root.right);System.out.print(root.val+" ");}
这里的代码的递归思路就是完美的按照我们遍历方向来的,
先递归,后访问
,小编在这里就 不赘述 了
<2>. 后序遍历的非递归实现
// 非递归的后序遍历public void lastDisplayNo (TreeNode root) {Stack<TreeNode> stack=new Stack<>();TreeNode cur=root;TreeNode flg=null;while (cur != null || !stack.empty()) {while (cur != null) {stack.add(cur);cur=cur.left;}TreeNode top=stack.peek();if (top.right == null || flg==top.right) {System.out.print(top.val+" ");flg=top;stack.pop();} else {cur=top.right;}}System.out.println();}
非递归的实现思路:
我们先定义一个栈,用来存放节点, 而这里存放的节点有可能是 左子树的节点
,也有可能是 右子树的节点
- 先向左走,让左子树的节点先入栈
- 然后 查看栈顶元素,如果栈顶元素的
右节点
为 null , 我们就打印(访问)
该节点,
- 如果栈顶元素的
右节点
不为 null , 我们就 让 该节点 向右走 , 并且入栈
- 以此循环往复,当
栈为 null
并且 节点 cur 也为 null , 说明我们已经遍历完这个 二叉树所有的节点
鱼式疯言
无论是 非递归还是递归实现 对二叉树的 后序遍历
-
小伙伴们只需要记住一点: 后序遍历 一定是
两边先走完
,最后回到我们的根节点才访问
的 -
小伙伴们一定要把每个节点都看出一颗独立的树。每个节点 都是一个
独立的根节点
来理解我们的三大遍历
TreeNode flg =nullif (top.right == null || flg==top.right) {System.out.print(top.val+" ");flg=top;stack.pop();}
细节处理: 我们需要用一个 flg
来记录上一个已经 访问过 的节点,判断 是否访问过, 防止再次让 top 向右走,继续入栈, 否则会进入 死循环
四. 层序遍历
谈及完前面的 三大遍历
, 这些是我们 操作二叉树的根本
,但还有还要介绍一种 比较特殊的遍历
1. 层序遍历的特点
二叉树
层序遍历
的方向是从 根节点,按照 从上而下,从左到右 的顺序进行遍历 二叉树的每一个节点
动画演示
2. 层序遍历的实现
/*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val = val; }* TreeNode(int val, TreeNode left, TreeNode right) {* this.val = val;* this.left = left;* this.right = right;* }* }*/class Solution {List<List<Integer>> S=new ArrayList<List<Integer>>();public List<List<Integer>> levelOrder(TreeNode root) {if(root==null) {return S;}creatOrder(root,0);return S;}public void creatOrder(TreeNode root,int i) {if(root==null) {return ;}if(S.size()==i) {S.add(new ArrayList<Integer>());}S.get(i).add(root.val);creatOrder(root.left,i+1);creatOrder(root.right,i+1);}
}
具体实现步骤:
-
我们用一个
二维数组(二维顺序表)
来存储每一个节点,二叉树 每一层代表是二维数组的每一行
, 在这二叉树每一层的行中,从左往右的节点 代表二维数组的每一列
-
当二叉树从
左子树
开始递归, 意味着先存储 每一行 的二叉树的节点
-
当二叉树向
右子树
开始递归, 意味着存储 每一列 的二叉树的节点
-
最终当整个二叉树完全递归就意味着 全部的节点都存储在 这个二维数组 (二维顺序表) 中
鱼式疯言
if(S.size()==i) {S.add(new ArrayList<Integer>());}
细节处理
每新添加
一行数据
,需要扩容
,就是需要再 实例化一个顺序表 ,已有的行数就不需要了
小伙伴们有没有发现,二叉树的层序遍历,本质上和我们的 完全二叉树的定义 是一样的,都是满足
自上而下,自左而右
的特点
六. 二叉树遍历的应用
学习完了 二叉树遍历,小伙伴们是时候 牛刀小试
一下了 💞 💞 💞
1. 习题一:
1.某完全二叉树按层次输出(同一层从左到右)的序列为 ABCDEFGH 。该完全二叉树的前序序列为()
A: ABDHECFG
B: ABCDEFGH
C: HDBEAFCG
D: HDEBFGCA
题目解析
我们知道了二叉树的 层序遍历
, 并且小伙伴们还有没有注意一个条件就是 完全二叉树
完全二叉树的特点就是
自上而下
,自左而右
节点不间断
那么我们不妨画个草图吧
画出草图,我们就很明显的知道了,答案选: A
2. 习题二:
2.二叉树的先序遍历和中序遍历如下:先序遍历:EFHIGJK;中序遍历:HFIEJKG.则二叉树根结点为()
A: E
B: F
C: G
D: H
题目解析:
此题题目就是 答案, 我们知道前序遍历, 是从 根节点
开始的 , 所以 第一个访问出来的节点
就是我们的 根节点
故:答案选:A
3. 习题三:
3.设一课二叉树的中序遍历序列:badce,后序遍历序列:bdeca,则二叉树前序遍历序列为()
A: adbce
B: decab
C: debac
D: abcde
题目解析:
此题的精髓就在于,我们要根据 中序遍历 和 后序遍历 ,画出草图, 根据草图得到我们的 前序遍历
画草图的方法:
方法: 先根据后序遍历寻找 根节点
对于 后序遍历
来说:根节点是从右往左 , 然后结合 中序遍历的特点
来确定 左右节点 的位置
故此题答案选: D
4. 习题四:
4.某二叉树的后序遍历序列与中序遍历序列相同,均为 ABCDEF ,则按层次输出(同一层从左到右)的序列为()
A: FEDCBA
B: CBAFED
C: DEFCBA
D: ABCDEF
题目解析 :
此题的精髓就在于,我们要根据 中序遍历 和 后序遍历 ,画出草图, 根据草图得到我们的 层序遍历
依照上一题的方法,我们成功画出草图,最终得到我们的层序遍历
故答案选: A
鱼式疯言
独家秘方:
- 对于我们已知
前序和中序
遍历,我们的方法就是根据 前序遍历从左往右 找根节点,然后结合 中序遍历画出草图
- 对于 我们已知的
后序和中序
遍历, 我们的方法是 根据 后序遍历 从右往左找根节点 , 然后结合中序遍历画出草图
对于上述题目来说, 画图是 根本
总结
-
. 二叉树的遍历初识: 我们通过基本的概念知道了二叉树是通过一定
规则和方向
来遍历我们 每一个节点 -
. 前序遍历 : 本源是 根-左-右的方向遍历
-
. 中序遍历: 本源是 左-根-右的方向遍历
-
.后序遍历 : 本质上还是根据 左-右-根的方向遍历
-
. 层序遍历: 遵循一个 自上而下, 自左而右 的顺序遍历
-
. 二叉树遍历的应用 : 我们主打一个对于这四种遍历的性质的理解和应用,来画图解题
如果觉得小编写的还不错的咱可支持 三连 下 (定有回访哦) , 不妥当的咱请评论区 指正
希望我的文章能给各位宝子们带来哪怕一点点的收获就是 小编创作 的最大 动力 💖 💖 💖