二叉树的前序、中序、后序遍历我想大家应该都很熟悉了,那我们今天就来讲一下二叉树的层次遍历。
二叉树的前序、中序、后序遍历需要用到栈(递归的过程也就是一个栈),层次遍历需要借助队列这个数据结构。
深度优先遍历(递归版)
package com.lc.javabase;
/**
* @author liuchao02
* @ClassName: MyTree
* @Description: 树 demo
* @date 2021/2/22 17:02
*/
public class MyTree {
int data;
MyTree leftNode;
MyTree rightNode;
public MyTree(int data) {
this.data = data;
leftNode = null;
rightNode = null;
}
public static void main(String[] args) {
}
private void add(MyTree myTree, int data) {
if (myTree.data > data) {
if (myTree.leftNode == null) {
myTree.leftNode = new MyTree(data);
} else {
add(myTree.leftNode, data);
}
} else {
if (myTree.rightNode == null) {
myTree.rightNode = new MyTree(data);
} else {
add(myTree.rightNode, data);
}
}
}
//先序遍历
public static void first(MyTree myTree) {
if (myTree != null) {
System.out.println(myTree.data + " ");
first(myTree.leftNode);
first(myTree.rightNode);
}
}
//中序遍历
public static void mid(MyTree myTree) {
if (myTree != null) {
first(myTree.leftNode);
System.out.println(myTree.data + " ");
first(myTree.rightNode);
}
}
//中序遍历
public static void back(MyTree myTree) {
if (myTree != null) {
first(myTree.leftNode);
first(myTree.rightNode);
System.out.println(myTree.data + " ");
}
}
}
深度优先遍历(非递归版,利用栈Stack数据结构实现)
二叉树前序遍历的非递归实现
实现思路,先序遍历是要先访问根节点,然后再去访问左子树以及右子树,这明显是递归定义。但这里是用栈来实现的首先需要先从栈顶取出节点,然后访问该节点,如果该节点不为空,则访问该节点,同时把该节点的右子树先入栈,然后左子树入栈。循环结束的条件是栈中不在有节点。即 !s.empty()
public void preOrder(Node root) {
Stack<Node> s = new Stack<Node>();
s.push(root);
Node p = null;
while (!s.empty()) {
p = s.pop();
if (p != null) {
System.out.print(p.val+" ");
s.push(p.right);
s.push(p.left);
}
}
}
二叉树的中序遍历非递归实现
实现思路,中序遍历是要先遍历左子树,然后根节点,最后遍历右子树。所以需要先把根节点入栈然后在一直把左子树入栈直到左子树为空,此时停止入栈。栈顶节点就是我们需要访问的节点,取栈顶节点p并访问。然后改节点可能有右子树,所以访问完节点p后还要判断p的右子树是否为空,如果为空则接下来要访问的节点在栈顶,所以将p赋值为null。如果不为空则将p赋值为其右子树的值。 循环结束的条件是p不为空或者栈不为空。
public void inOrder(Node root) {
Stack<Node> s = new Stack<Node>();
Node p = root;
do {
while (p != null) {
s.push(p);
p = p.left;
}
p = s.pop();
System.out.print(p.val+" ");
if (p.right != null) {
p = p.right;
}
else p = null;
} while(p != null || !s.empty());
}
二叉树的后序遍历里非递归实现
实现思路,在进行后序遍历的时候是先要遍历左子树,然后在遍历右子树,最后才遍历根节点。所以在非递归的实现中要先把根节点入栈 然后再把左子树入栈直到左子树为空,此时停止入栈。此时栈顶就是需要访问的元素,所以直接取出访问p。在访问结束后,还要判断被访问的节点p是否为栈顶节点的左子树,如果是的话那么还需要访问栈顶节点的右子树,所以将栈顶节点的右子树取出赋值给p。如果不是的话则说明栈顶节点的右子树已经访问完了,那么现在可以访问栈顶节点了,所以此时将p赋值为null。判断结束的条件是p不为空或者栈不为空,若果两个条件都不满足的话,说明所有节点都已经访问完成。
public void postOrder(Node root) {
Stack<Node> s = new Stack<Node>();
Node p = root;
while (p != null || !s.empty()) {
while(p != null) {
s.push(p);
p = p.left;
}
p = s.pop();
System.out.print(p.val+" ");
//这里需要判断一下,当前p是否为栈顶的左子树,如果是的话那么还需要先访问右子树才能访问根节点
//如果已经是不是左子树的话,那么说明左右子书都已经访问完毕,可以访问根节点了,所以讲p复制为NULL
//取根节点
if (!s.empty() && p == s.peek().left) {
p = s.peek().right;
}
else p = null;
}
}
最后附上Node节点的Java类代码示例
//树的Node节点类
class Node {
public int val; //节点值
public Node left; //左子树
public Node right; //右子树
public Node() {}
public Node(int val, Node left, Node right) {
this.val = val;
this.left = left;
this.right = right;
}
}
层次遍历的思路
我们给出一个二叉树:

这棵二叉树的层次遍历次序为:A、B、C、D、F、G
以人的思维来看层次遍历貌似比前、中、后序遍历更加简单易懂,但是程序到底如何实现这样的效果呢?这里我们需要用到一个数据结构,那就是队列。
核心思想:每次出队一个元素,就将该元素的孩子节点加入队列中,直至队列中元素个数为0时,出队的顺序就是该二叉树的层次遍历结果。
可能这句话不太好理解,我们依旧是以画图的形式来表达:
1. 初始状态下,队列中只保留根节点的元素:

2. 当A出队时,将A的孩子节点加入队列中:

3.重复上面的动作,队首元素出队时,将孩子节点加入队尾.......



...........
相信看了上面的过程,大家对层次遍历的思路已经有了清晰的认识,那么我们就需要用代码来实现它了:
层次遍历的实现
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
public class MyTree {
public static class TreeNode {
public Object key;
public TreeNode parent;
public List<TreeNode> childrens = new ArrayList<>();
public TreeNode(Object key) {
this.key = key;
}
}
private int size = 0;
private TreeNode root;
public MyTree(TreeNode root) {
this.root = root;
size++;
}
/**
* 层次遍历
*
* @param treeNode
* @return
*/
public List<TreeNode> levelOrder(TreeNode treeNode) {
/**
* 层次遍历使用到了广度优先搜索,技巧:深度优先用递归,广度优先用队列。
*/
Queue<TreeNode> queue = new LinkedList<>();
List<TreeNode> list = new LinkedList<>();
queue.add(treeNode);
while (queue.size() > 0) {
//出一个,进n个
//出一个
TreeNode node = queue.poll();
list.add(node);
//进n个
List<TreeNode> childens = node.childrens;
for (TreeNode childNode : childens) {
queue.add(childNode);
}
}
return list;
}
}
注:代码中实现的是一个普通的树,不是一颗二叉树。
或者实现代码如下
package tree.层次遍历二叉树;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Stack;
class Solution {
public static void main(String[] args) {
TreeNode t1 = new TreeNode(1);
TreeNode t2 = new TreeNode(2);
TreeNode t3 = new TreeNode(3);
TreeNode t4 = new TreeNode(4);
TreeNode t5 = new TreeNode(5);
t1.left=t2;
t1.right=t3;
t2.left=t4;
t2.right=t5;
System.out.println();
System.out.println("层次遍历:");
List<List<Integer>> lists = levelOrder(t1);
System.out.println("结果:"+lists.toString());
System.out.println("遍历结果:");
lists.stream().forEach(li -> {
li.stream().forEach(ll -> {
System.out.print(ll.intValue()+" ");
});
System.out.println();
});
}
static public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> lists = new ArrayList<>();
//如果直接传入的是空,那就直接返回
if(root==null)
return lists;
//利用队列的特性来存储每层的节点
LinkedList<TreeNode> queue = new LinkedList<>();
//默认根节点已经加入到队列
queue.add(root);
while(!queue.isEmpty()){
List<Integer> list = new ArrayList<>();
int n = queue.size();//记录当前层次的数量
int index = 0;
//只要每层遍历的次数符合每层的数量就算是遍历的这同一层的节点。
while (index<n){
root = queue.pop();
//添加到每一层的List中
list.add(root.val);
if(root.left!=null) {
queue.add(root.left);
}
if(root.right!=null) {
queue.add(root.right);
}
//索引++ 遍历同一层次后边的节点
index++;
}
//把每一层的节点加入到最终的List中
lists.add(list);
}
return lists;
}
}
class TreeNode {
int val;//每个节点存放的数据
TreeNode left;//左节点
TreeNode right;//右节点
TreeNode(int x) { val = x; }
}
二叉树的锯齿形层次遍历
力扣
https://leetcode-cn.com/problems/binary-tree-zigzag-level-order-traversal/思路:在上述层次遍历的基础上, 利用level层级判断 返回追加数据的顺序 "头插法"还是"尾插法"
我们可以给每一层都标上一个号。
那我们就从1开始
如果这个层是奇数说明是第一层
如果是偶数说明是第二层
然后层数+1
然后循环执行
当判断是奇数还是偶数的时候执行不同的操作。
如果是奇数,那就把数据使用尾插法插入的list,说白了就是追加数据
如果是偶数,那就吧数据使用头插法插入list
最终就实现了这个题。
核心代码如下:
static public List<List<Integer>> levelOrder(TreeNode root) {
List<List<Integer>> lists = new ArrayList<>();
if(root==null) return lists;
TreeNode treeNode = root;
LinkedList<TreeNode> queue = new LinkedList<>();
queue.add(treeNode);
int ceng = 1;//记录层次
while(!queue.isEmpty()){
List<Integer> list = new ArrayList<>();
int n = queue.size();
int index =0;
while (n>index){
//出栈
TreeNode node = queue.pop();
//说明是第一层 如果第二层是会ceng+1,说明切换到了第二层
if(ceng%2!=0){
//追加数据,也就是尾插法
list.add(node.val);
//说明是二层
}else{
//头插法
list.add(0,node.val);
}
if(node.left!=null){
queue.add(node.left);
}
if(node.right!=null){
queue.add(node.right);
}
index++;
}
ceng++;//每一层结束都+1
lists.add(list);
}
return lists;
}
或者如下:
始终保持队列中顺序为:每层的从左往右
- 奇数层,从左往右弹出,从右往左添加
- 偶数层,从右往左弹出,从左往右添加
class Solution {
public List<List<Integer>> zigzagLevelOrder(TreeNode root) {
List<List<Integer>> res = new ArrayList<>();
if(root == null){
return res;
}
ArrayDeque<TreeNode> queue = new ArrayDeque<>();
queue.add(root);
int count = 1;
while(!queue.isEmpty()){
int size = queue.size();
List<Integer> curList = new ArrayList<>();
for(int i = 0;i<size;i++){
//奇数层,从左往右弹,从右往左添加
if(count % 2 == 1){
TreeNode cur = queue.pollFirst();
curList.add(cur.val);
//保证结点在队列中的顺序,先加左结点到Last,再加右结点到Last
if(cur.left != null){
queue.addLast(cur.left);
}
if(cur.right != null){
queue.addLast(cur.right);
}
//偶数层,从右往左弹,从左往右添加
}else {
TreeNode cur = queue.pollLast();
curList.add(cur.val);
//保证结点在队列中的顺序,先加右结点到First,再加左结点到First
if(cur.right != null){
queue.addFirst(cur.right);
}
if(cur.left != null){
queue.addFirst(cur.left);
}
}
}
if(!curList.isEmpty()){
res.add(curList);
}
count++;
}
return res;
}
}

本文详细讲解了二叉树的层次遍历算法,通过非递归方式演示如何使用队列实现,包括先序、中序和后序遍历,并以实例演示了层次遍历在特定场景的应用,如锯齿形层次遍历。
4558

被折叠的 条评论
为什么被折叠?



