快捷搜索:  汽车  科技

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)接下来我们这几种遍历算法,一个一个的认识。package tree; /** * @Auther: truedei * @Date: 2020 /20-6-7 17:39 * @Description: */ public class TestTrueDeiTree { public static void main(String[] args) { TreeNode t1 = new TreeNode(3); TreeNode t2 = new TreeNode(9); TreeNode t3 = new TreeNode(20); TreeNode t4 = new TreeNode(15); TreeNode t5 = new TreeNode(7); t1.left

推荐学习
  • 牛掰!“基础-中级-高级”Java程序员面试集结,看完献出我的膝盖
  • 数据结构与算法,程序员必过的坎?不掌握一定挤不进BATJ的神技?

我是怎么调试出来二叉树的遍历(超精彩配图),从此遍历不再愁了!

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(1)

前言

二叉树遍历(Traversing binary tree)是指从根节点触发,按照某种次序依次访问二叉树中所有的结点,使得每个结点被依次访问且仅仅被访问一次。

二叉树的遍历有四种方式,分别为:先序遍历、中序遍历、后序遍历、层次遍历。

一、准备工作

在做实验之前先准备一个二叉树的结构,这样方便进行实验。

(一)定义二叉树的数据结构

package tree; /** * @Auther: truedei * @Date: 2020 /20-6-5 11:59 * @Description: */ public class TreeNode { int val;//每个节点存放的数据 TreeNode left;//左节点 TreeNode right;//右节点 TreeNode(int x) { val = x; } }(二)手工构造一个二叉树

至于为什么是手工,因为不想把你搞混了,这篇文章主要是遍历,对于构造二叉树的方法可以自行研究一下。

package tree; /** * @Auther: truedei * @Date: 2020 /20-6-7 17:39 * @Description: */ public class TestTrueDeiTree { public static void main(String[] args) { TreeNode t1 = new TreeNode(3); TreeNode t2 = new TreeNode(9); TreeNode t3 = new TreeNode(20); TreeNode t4 = new TreeNode(15); TreeNode t5 = new TreeNode(7); t1.left=t2; t1.right=t3; t3.left=t4; t3.right=t5; } }

构造之后的二叉树的结构图:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(2)

二、二叉树的遍历讲解

接下来我们这几种遍历算法,一个一个的认识。

四种遍历的概念:

(1)先序遍历:先访问根节点,再访问左子树,最后访问右子树;可以说为”根-左-右“。

(2)后序遍历:先左子树,再右子树,最后根节点;可以说为”左-右-根“。

(3)中序遍历:先左子树,再根节点,最后右子树;可以说为”左-根-右“。

(4)层序遍历:每一层从左到右访问每一个节点。

(一)前序遍历

1、前序遍历概念

前序遍历:先访问根节点,再访问左子树,最后访问右子树;可以说为”根-左-右“。

按照:先访问根节点,再访问左子树,最后访问右子树的顺序如果用图来描述一下的话,是这样子的;

2、图解前序遍历

1、首先我们有一颗如下所示的二叉树,这就是我们的初始值:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(3)

方便理解,为此我做了一个GIF的图:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(4)

遍历的顺序:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(5)

通过这个规则我们得到前序遍历结果:3、9、20、15、7

3、代码实现前序遍历(1)递归实现

/** 根-> 左-> 右 * 前序遍历 * @param root */ public static void PreOrder(TreeNode root) { //结束的条件 if (root==null) return; System.out.print(root.val " "); //使用递归进行遍历左孩子 PreOrder(root.left); //递归遍历右孩子 PreOrder(root.right); }

结果:

3 9 20 15 7

(2)非递归实现

1.首先申请一个新的栈,记为stack;

2.声明一个结点treeNode,让其指向root结点;

3.如果treeNode的不为空,将treeNode的值打印,并将treeNode入栈,然后让treeNode指向4.treeNode的左结点,

5.重复步骤3,直到treenode为空;

6.然后出栈,让treeNode指向treeNode的右孩子

7.重复步骤3,直到stack为空.

/**根-->左-->右 * 非递归前序遍历 * * * 1,首先申请一个新的栈,记为stack; * 2,声明一个结点treeNode,让其指向node结点; * 3,如果treeNode的不为空,将treeNode的值打印,并将treeNode入栈,然后让treeNode指向treeNode的左结点, * 4,重复步骤3,直到treenode为空; * 5,然后出栈,让treeNode指向treeNode的右孩子 * 6,重复步骤3,直到stack为空. * * @param root */ public static void preOrderStack(TreeNode root){ //利用栈的特性(先进的后出),用于存储节点数据,一层一层的往上冒 Stack<TreeNode> stack = new Stack<>(); //相当于临时的变量,记录当前的所在节点 TreeNode treeNode = root; //两个条件满足一个即可继续执行,退出的条件是当前的节点为null和栈也空了,说明没有数据了 //栈空了说明冒到了最上一层,最上一层也遍历成了,就空了 while (treeNode != null || !stack.isEmpty()){ //迭代访问节点的左孩子,并入栈 //一直遍历最左边的节点 while (treeNode != null){ //先根 System.out.print(treeNode.val " "); stack.push(treeNode); //遍历完根,然后还是遍历最左边的。。如果还有的话,还是遍历最左边的 treeNode = treeNode.left; } //如果节点没有左孩子,则弹出栈顶节点,访问节点右孩子 if(!stack.isEmpty()){ treeNode = stack.pop(); treeNode = treeNode.right; } } }4、使用IDEA调试java代码进一步查看执行过程

现在我的代码是这样子的:

package tree; /** * @Auther: truedei * @Date: 2020 /20-6-7 17:39 * @Description: */ public class TestTrueDeiTree { public static void main(String[] args) { TreeNode t1 = new TreeNode(3); TreeNode t2 = new TreeNode(9); TreeNode t3 = new TreeNode(20); TreeNode t4 = new TreeNode(15); TreeNode t5 = new TreeNode(7); t1.left=t2; t1.right=t3; t3.left=t4; t3.right=t5; PreOrder(t1); } /** * 前序遍历 * @param root */ public static void PreOrder(TreeNode root) { //先序遍历 if (root==null) return; System.out.print(root.val " "); //使用递归进行遍历左孩子 PreOrder(root.left); //递归遍历右孩子 PreOrder(root.right); } } //树结构 class TreeNode { int val;//每个节点存放的数据 TreeNode left;//左节点 TreeNode right;//右节点 TreeNode(int x) { val = x; } }

为了方便调试,我们先在下面这几个地方打上断点

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(6)

第1次:

因为是初次遍历,所以root肯定不为null,所以执行打印的这条语句;

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(7)

打印了3,就是我们想要的结果

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(8)

继续调试,更详细的解析,就看图吧:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(9)

继续下一步

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(10)

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(11)

再次点击下一步之后,传入的是null,所以就不会再继续执行剩下的代码了

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(12)

继续点击下一步:

至于为什么会调用执行了遍历右孩子的代码。原因有:

1、程序的代码都是顺序执行的

2、和JVM相关,如果你学过JVM,则这个地方为什么会执行就会很清晰了

3、遍历3的左孩子的结束了,自然而然的销毁了之前的代码,然后就执行到了

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(13)

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(14)

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(15)

继续点下一步,会又一次的进入,并且把20的left节点传入

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(16)

这个应该就不用多解释了,上面解释的很清楚了

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(17)

继续下一步会打印出当前节点的值,并调到下一个Debug处

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(18)

继续下一步会为null,所以退出当前的方法

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(19)

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(20)

发现还是为null

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(21)

再次点击下一步,就会结束掉这次的了

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(22)

再次点击下一步,会销毁掉刚才运行的方法,回到上一次进入时的状态,这次就该轮到right了

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(23)

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(24)

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(25)

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(26)

再点一次,main方法也会执行结束,并将其销毁,从而得到了我们想要的结果:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(27)

(二)中序遍历1、中序遍历概念

中序遍历:先左子树,再根节点,最后右子树;可以说为”左-根-右“。

按照:先左子树,再根节点,最后右子树的顺序如果用图来描述一下的话,是这样子的;

2、图解中序遍历

1、首先我们有一颗如下所示的二叉树,这就是我们的初始值:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(28)

执行顺序:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(29)

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(30)

3、代码实现中序遍历(1)递归实现

因为是递归,代码也很简单,修改一下位置就好了。

/** 左-> 根-> 右 * 中序遍历 * @param root */ public static void MidOrder(TreeNode root) { //先序遍历 if (root==null) return; //使用递归每次都先进行遍历左孩子 MidOrder(root.left); System.out.print(root.val " "); //递归遍历右孩子 MidOrder(root.right); }

(2)非递归实现

1.申请一个新栈,记为stack,申请一个变量treeNode,初始时令treeNode为头节点;

2.先把treeNode节点压入栈中,对以treeNode节点为头的整棵子树来说,依次把整棵树的左子树压入栈中,即不断令treeNode=treeNode.leftChild,然后重复步骤2;

3.不断重复步骤2,直到发现treeNode为空,此时从stack中弹出一个节点记为treeNode,打印node的值,并让treeNode= treeNode.right,然后继续重复步骤2;

当stack为空并且treeNode为空时结束

/** 左-->根-->右 * 非递归实现中序遍历 * @param root */ public static void MidOrderStack(TreeNode root){ Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode treeNode = root; while(treeNode!=null || !stack.isEmpty()){ while(treeNode != null){ stack.push(treeNode); treeNode = treeNode.left; } if(!stack.isEmpty()){ treeNode = stack.pop(); System.out.print(treeNode.val " "); treeNode = treeNode.right; } } }4、调试略

整个过程和前序遍历都是差不多的,可以自己试着调试一下,这里就不在啰嗦了。

(三)后序遍历1、后序遍历概念

后序遍历:先左子树,再右子树,最后根节点;可以说为”左-右-根“。

按照:先左子树,再右子树,最后根节点的顺序如果用图来描述一下的话,是这样子的;

2、图解后序遍历

1、首先我们有一颗如下所示的二叉树,这就是我们的初始值:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(31)

遍历到顺序和结果:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(32)

动态过程图:

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(33)

3、代码实现后序遍历(1)递归实现

/** 左-->右-->根 * 后序遍历 * @param root */ public static void BackOrder(TreeNode root) { //先序遍历 if (root==null) return; //使用递归每次都先进行遍历左孩子 BackOrder(root.left); //递归遍历右孩子 BackOrder(root.right); //根 System.out.print(root.val " "); }

(2)非递归实现

/** 左-->右-->根 * 非递归实现后序遍历 * @param root */ public static void BackOrderStack(TreeNode root){ Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode treeNode = root; TreeNode lastVisit = null; //标记每次遍历最后一次访问的节点 //节点不为空,结点入栈,并且指向下一个左孩子 while(treeNode!=null || !stack.isEmpty()){ while(treeNode!=null){ stack.push(treeNode); treeNode = treeNode.left; } //栈不为空 if(!stack.isEmpty()){ //出栈 treeNode = stack.pop(); /** * 这块就是判断treeNode是否有右孩子, * 如果没有,则输出treeNode.val,让lastVisit指向treeNode,并让treeNode为空 * 如果有右孩子,将当前节点继续入栈,treeNode指向它的右孩子 继续重复循环 */ if(treeNode.right == null || treeNode.right == lastVisit) { System.out.print(treeNode.val " "); lastVisit = treeNode; treeNode = null; }else{ stack.push(treeNode); treeNode = treeNode.right; } } } }4、调试略

整个过程和前序遍历都是差不多的,可以自己试着调试一下,这里就不在啰嗦了。

(四)层次遍历

1.首先申请一个新的队列,记为queue;

2.将根结点root压入queue中;

3.每次从queue中出队,并赋值给root,作为下一次的根,然后打印root.val的值,如果root左孩子4.不为空,则将左孩子入队;如果root的右孩子不为空,则将右孩子入队;

重复步骤3,直到queue为空。

/** * 层次遍历 * @param root */ public static void levelOrder(TreeNode root){ LinkedList<TreeNode> queue = new LinkedList<>(); queue.add(root); while(!queue.isEmpty()){ root = queue.pop(); System.out.print(root.val " "); if(root.left!=null) queue.add(root.left); if(root.right!=null) queue.add(root.right); } }

结果:

3 9 20 15 7 三、总结

遍历二叉树可以使用递归的方式和非递归的方式来遍历。

二叉树的遍历的步骤(我是怎么调试出来二叉树的遍历)(34)

四种遍历:

(1)先序遍历:先访问根节点,再访问左子树,最后访问右子树;可以说为”根-左-右“。

(2)后序遍历:先左子树,再右子树,最后根节点;可以说为”左-右-根“。

(3)中序遍历:先左子树,再根节点,最后右子树;可以说为”左-根-右“。

(4)层序遍历:每一层从左到右访问每一个节点。

所有代码:

package tree; import java.util.LinkedList; import java.util.Stack; /** * @Auther: truedei * @Date: 2020 /20-6-7 17:39 * @Description: //preorder,midorder,backorder */ public class TestTrueDeiTree { public static void main(String[] args) { TreeNode t1 = new TreeNode(3); TreeNode t2 = new TreeNode(9); TreeNode t3 = new TreeNode(20); TreeNode t4 = new TreeNode(15); TreeNode t5 = new TreeNode(7); t1.left=t2; t1.right=t3; t3.left=t4; t3.right=t5; System.out.println("递归前序遍历:"); PreOrder(t1); System.out.println(); System.out.println("递归中序遍历:"); MidOrder(t1); System.out.println(); System.out.println("递归后序遍历:"); BackOrder(t1); System.out.println(); System.out.println("非递归前序遍历:"); preOrderStack(t1); System.out.println(); System.out.println("非递归中序遍历:"); MidOrderStack(t1); System.out.println(); System.out.println("非递归后序遍历:"); BackOrderStack(t1); System.out.println(); System.out.println("层次遍历:"); levelOrder(t1); } /**根-->左-->右 * 递归前序遍历 * @param root */ public static void PreOrder(TreeNode root) { if (root==null) return; System.out.print(root.val " "); //使用递归进行遍历左孩子 PreOrder(root.left); //递归遍历右孩子 PreOrder(root.right); } /**根-->左-->右 * 非递归前序遍历 * * * 1,首先申请一个新的栈,记为stack; * 2,声明一个结点treeNode,让其指向node结点; * 3,如果treeNode的不为空,将treeNode的值打印,并将treeNode入栈,然后让treeNode指向treeNode的右结点, * 4,重复步骤3,直到treenode为空; * 5,然后出栈,让treeNode指向treeNode的右孩子 * 6,重复步骤3,直到stack为空. * * @param root */ public static void preOrderStack(TreeNode root){ //利用栈的特性(先进的后出),用于存储节点数据,一层一层的往上冒 Stack<TreeNode> stack = new Stack<>(); //相当于临时的变量,记录当前的所在节点 TreeNode treeNode = root; //两个条件满足一个即可继续执行,退出的条件是当前的节点为null和栈也空了,说明没有数据了 //栈空了说明冒到了最上一层,最上一层也遍历成了,就空了 while (treeNode != null || !stack.isEmpty()){ //迭代访问节点的左孩子,并入栈 //一直遍历最左边的节点 while (treeNode != null){ //先根 System.out.print(treeNode.val " "); stack.push(treeNode); //遍历完根,然后还是遍历最左边的。。如果还有的话,还是遍历最左边的 treeNode = treeNode.left; } //如果节点没有左孩子,则弹出栈顶节点,访问节点右孩子 if(!stack.isEmpty()){ treeNode = stack.pop(); treeNode = treeNode.right; } } } /** 左-->根-->右 * 中序遍历 * @param root */ public static void MidOrder(TreeNode root) { //先序遍历 if (root==null) return; //使用递归每次都先进行遍历左孩子 MidOrder(root.left); System.out.print(root.val " "); //递归遍历右孩子 MidOrder(root.right); } /** 左-->根-->右 * 中序遍历 * *1. 申请一个新栈,记为stack,申请一个变量treeNode,初始时令treeNode为头节点; *2. 先把treeNode节点压入栈中,对以treeNode节点为头的整棵子树来说,依次把整棵树的左子树压入栈中,即不断令treeNode=treeNode.leftChild,然后重复步骤2; *3. 不断重复步骤2,直到发现treeNode为空,此时从stack中弹出一个节点记为treeNode,打印node的值,并让treeNode= treeNode.right,然后继续重复步骤2; *4. 当stack为空并且treeNode为空时结束。 * @param root */ public static void MidOrderStack(TreeNode root){ Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode treeNode = root; while(treeNode!=null || !stack.isEmpty()){ while(treeNode != null){ stack.push(treeNode); treeNode = treeNode.left; } if(!stack.isEmpty()){ treeNode = stack.pop(); System.out.print(treeNode.val " "); treeNode = treeNode.right; } } } /** 左-->右-->根 * 后序遍历 * @param root */ public static void BackOrder(TreeNode root) { //先序遍历 if (root==null) return; //使用递归每次都先进行遍历左孩子 BackOrder(root.left); //递归遍历右孩子 BackOrder(root.right); //根 System.out.print(root.val " "); } /** 左-->右-->根 * 非递归实现后序遍历 * @param root */ public static void BackOrderStack(TreeNode root){ Stack<TreeNode> stack = new Stack<TreeNode>(); TreeNode treeNode = root; TreeNode lastVisit = null; //标记每次遍历最后一次访问的节点 //节点不为空,结点入栈,并且指向下一个左孩子 while(treeNode!=null || !stack.isEmpty()){ while(treeNode!=null){ stack.push(treeNode); treeNode = treeNode.left; } //栈不为空 if(!stack.isEmpty()){ //出栈 treeNode = stack.pop(); /** * 这块就是判断treeNode是否有右孩子, * 如果没有,则输出treeNode.val,让lastVisit指向treeNode,并让treeNode为空 * 如果有右孩子,将当前节点继续入栈,treeNode指向它的右孩子 继续重复循环 */ if(treeNode.right == null || treeNode.right == lastVisit) { System.out.print(treeNode.val " "); lastVisit = treeNode; treeNode = null; }else{ stack.push(treeNode); treeNode = treeNode.right; } } } } /** * 层次遍历 * @param root */ public static void levelOrder(TreeNode root){ LinkedList<TreeNode> queue = new LinkedList<>(); queue.add(root); while(!queue.isEmpty()){ root = queue.pop(); System.out.print(root.val " "); if(root.left!=null) queue.add(root.left); if(root.right!=null) queue.add(root.right); } } } class TreeNode { int val;//每个节点存放的数据 TreeNode left;//左节点 TreeNode right;//右节点 TreeNode(int x) { val = x; } }

作者:TrueDei

原文链接:https://blog.csdn.net/qq_17623363/article/details/106612418

猜您喜欢: