vlambda博客
学习文章列表

两种方法实现二叉树的层序遍历

关于二叉树的内容,我觉得二叉树最核心的地方就是它的几种遍历方式,基本上所有的问题都是围绕着几种遍历方式来的。因此就在这总结一下这几种遍历方式。

前序遍历

一、简介

前序遍历(DLR),是二叉树遍历的一种,也叫做先根遍历、先序遍历,可记做根左右。

前序遍历首先访问根结点然后遍历左子树,最后遍历右子树。在遍历左、右子树时,仍然先访问根结点,然后遍历左子树,最后遍历右子树。

若二叉树为空则结束返回,否则:

1.访问根结点

2.前序遍历左子树

2.前序遍历右子树

如图:前序遍历结果:ABDECF


二、代码实现

1.递归实现

class TreeNode{//TreeNode类,后边同样 TreeNode left; TreeNode right; int val; TreeNode(int val){ this.val = val; }}
public class PreOrderTraversal { public void preOrder(TreeNode root){ if(root != null){ System.out.println(root.val);//打印根节点 preOrder(root.left);//访问左子树 preOrder(root.right);//访问右子树 } }}

2.非递归实现

方法一:根节点存入栈中打印根节点,然后访问这个根节点的左子树,左子树也是将左子树的根存入栈中打印根节点,依次往下直到左子树为空,再取出栈顶元素,栈顶元素(访问完左子树的根节点)作为新的根节点去访问右子树。

public void preorderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<>();//用一个栈来存放树中的节点 while(root != null || !stack.isEmpty()) {//只要当前节点不为空(即当前节点的左右子树没有访问完毕)或者栈中还有节点(还有节点没有访问) while (root != null) {//一直往左走 stack.push(root);//根节点入栈 System.out.println(root.val);//打印根节点 root = root.left;//访问左子树 } root = stack.pop();//取出根节点 root = root.right;//访问右子树 } }


方法二:我们知道前序遍历遵从根左右的顺序,所以我们打印根节点,并将它的右子树,左子树按照顺序压入栈中,每次只取出栈顶的一个节点,这样就可以保证所有的左子树都会在右子树之前取出并打印。


import java.util.Stack;

public class PreOrderTraversal { public void preorderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); if(root == null)//树为空 return; stack.add(root);//将根节点压入栈中 while(!stack.isEmpty()){//只要栈不为空,执行循环 TreeNode node = stack.pop();//取出栈顶元素打印,此时的栈顶元素是以node为根的子树的根 System.out.println(node.val); if(node.right != null)//将右子树压入栈中 stack.push(node.right); if(node.left != null)//将左子树压入栈中 stack.push(node.left); } }}


中序遍历

一、简介

中序遍历(LDR)是二叉树遍历的一种,也叫做中根遍历、中序周游。可记做左根右

中序遍历首先遍历左子树,然后访问根结点,最后遍历右子树。

若二叉树为空则结束返回,否则:

1.中序遍历左子树

2.访问根结点

3.中序遍历右子树

如图:中序遍历结果:DBEAFC

两种方法实现二叉树的层序遍历

二、代码实现

1.递归实现


public class InOrderTraversal { public void inorderTraversal(TreeNode root){ if(root != null){ inorderTraversal(root.left);//访问左子树 System.out.println(root.val);//打印根节点 inorderTraversal(root.right);//访问右子树 } }}



2.非递归实现

根前序遍历方法一思路一样,不同的只是先不打印根节点,而是先访问左子树,再打印根节点,访问右子树。


import java.util.Stack;

public class InOrderTraversal { public void inorderTraversal(TreeNode root){ Stack<TreeNode> stack = new Stack<>(); while(root != null || !stack.isEmpty()){//只要当前节点不为空(即当前节点的左右子树没有访问完毕)或者栈中还有节点(还有节点没有访问) while(root != null){ stack.push(root);//根节点入栈 root = root.left;//访问左子树 } root = stack.pop();//取出左子树的根节点 System.out.println(root.val);//打印根节点 root = root.right;//访问右子树 } }}



后序遍历

一、简介

首先遍历左子树,然后遍历右子树,最后遍历访问根结点,在遍历左、右子树时,仍然先遍历左子树,然后遍历右子树,最后遍历根结点。

若二叉树为空则结束返回,否则:

1.中序遍历左子树

2.中序遍历右子树

3.访问根结点

如图:后序遍历结果:DEBFCA

二、代码实现

1.递归实现

public class PostOrderTraversal { public void postOrder(TreeNode root){ if(root != null){ postOrder(root.left);//访问左子树 postOrder(root.right);//访问右子树 System.out.println(root.val);//打印根节点 } }}



2.非递归实现

方法一:常规遍历左右根,和前序、中序方法一类似。


import java.util.Stack;

public class PostOrderTraversal { public void postorderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); TreeNode node = root; TreeNode prev = null; while(node != null || !stack.isEmpty()){ while(node != null){ //访问左子树 stack.push(node); node = node.left; } //判断栈顶元素(根) node = stack.peek(); //1.如果此时的根的右子树为空node.right == null //2.如果此时的根的右子树已经访问过了node.right == prev(prev记录的是上次访问打印的节点) if(node.right == null || node.right == prev){ //打印根节点,并出栈,将打印过的节点从栈中删除 stack.pop(); System.out.println(node.val); //记录prev,表示以当前prev为根的子树已经访问过了 prev = node; //node置null就不会再次访问以node为根节点的左右子树,这里的node既然已经打印,说明它的左右子树早已访问完毕 node = null; }else{ //访问右子树 node = node.right; } } }}



方法二:和前序遍历的方法二类似,我们可以用一个线性表存放后序遍历的结果。我们知道后序遍历是左右根,但是我们可以反过来,一样先访问根,只是将根放在后边然后访问右子树放在根的前边左子树的后边,我们可以用线性表头插来实现这个操作,先访问根再右子树再左子树,只是每次都对线性表进行头插,这样最后的结果就还是左右根。


import java.util.LinkedList;import java.util.List;import java.util.Stack;

public class PostOrderTraversal { public List<Integer> postorderTraversal(TreeNode root) { Stack<TreeNode> stack = new Stack<>(); List<Integer> list = new LinkedList<>(); if(root == null) return list; stack.push(root); while(!stack.isEmpty()){ TreeNode node = stack.pop(); list.add(0, node.val);//头插此时根节点 if(node.left != null) stack.push(node.left); if(node.right != null) stack.push(node.right); } return list; }}



层序遍历

一、简介

除了先序遍历、中序遍历、后序遍历外,还可以对二叉树进行层序遍历。设二叉树的根节点所在层数为1,层序遍历就是从所在二叉树的根节点出发,首先访问第一层的树根节点,然后从左到右访问第2层上的节点,接着是第三层的节点,以此类推,自上而下,自左至右逐层访问树的结点的过程就是层序遍历。

如图:层序遍历结果:ABCDEF

二、代码实现


import java.util.LinkedList;import java.util.Queue;

public class LevelOrder { public void levelOrder(TreeNode root) { Queue<TreeNode> queue = new LinkedList<>(); if(root != null){//如果根节点不为空,将第一层根节点入队列 queue.offer(root); } while(!queue.isEmpty()){//只要队列不为空,执行循环 int num = queue.size();//记录此时队列的长度,此时的num代表了某一层一共有多少个节点,防止被后边入队的节点个数影响输出这一层的节点 for(int i = 0; i < num; i++){//对某一层的所有节点进行操作(从左到右) TreeNode topNode = queue.poll();//取出这一层第一个节点 System.out.println(topNode.val);//打印节点 if(topNode.left != null){//将此节点的左子树根节点入队列 queue.offer(topNode.left); } if(topNode.right != null){//将此节点的右子树根节点入队列 queue.offer(topNode.right); } } } }}


总结:

所谓遍历(Traversal)是指沿着某条搜索路线,依次对树中每个结点均做一次且仅做一次访问。访问结点所做的操作依赖于具体的应用问 题。遍历是二叉树上最重要的运算之一,是二叉树上进行其它运算之基础。一定要仔细体会其中的遍历过程。