简介
整理一下树的四种遍历方法,总体分两种 DFS 和 BFS
-
DFS 深度优先遍历
- 先序遍历:按照 根节点-左孩子-右孩子 的顺序遍历,即开始访问根节点
- 中序遍历:按照 左孩子-根节点-右孩子 的顺序遍历,即中间访问根节点
- 后序遍历:按照 左孩子-右孩子-根节点 的顺序遍历,即最后访问根节点
-
BFS广度优先遍历,
- 层序遍历:一层一层的访问节点
思路 & 实现
递归方式实现很简单,重点关注非递归版的三种DFS实现。
先序非递归遍历
- 这个和中序遍历有点像,只是这里访问在入栈的时候,中序访问在出栈的时候。
/**
* 非递归先序遍历二叉树
* @param root
*/
public static void preOrderTravalWithStack(TreeNode root) {
ArrayList res = new ArrayList();
TreeNode treeNode = root;
Stack<TreeNode> stack = new Stack<>();
while (treeNode != null || !stack.isEmpty()) {
//加入所有左孩子,同时访问该节点
while (treeNode != null) {
stack.push(treeNode);
res.add(treeNode.data);
treeNode = treeNode.left;
}
//如果没有左孩子,弹出栈顶节点,访问右孩子。
if (!stack.isEmpty()) {
treeNode = stack.pop();
treeNode = treeNode.right;
}
}
System.out.println(res);
}
- 这个方法是我认为合适的方法,
- 先把根节点入栈
- 出栈一个节点,右孩子不空入栈,左孩子不空入栈
- 直到栈中无元素
/**
* 先序非递归
* @param root
*/
public static void preOrderTravalWithStackV2(TreeNode root) {
ArrayList res = new ArrayList();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode tmp = null;
while (!stack.isEmpty()) {
tmp = stack.pop();
res.add(tmp.data);
if (tmp.right != null) {
stack.push(tmp.right);
}
if (tmp.left != null) {
stack.push(tmp.left);
}
}
System.out.println(res);
}
中序非递归遍历
思路:从根节点开始,左孩子不为空就一直入栈,为空的时候弹出栈中一个元素,转到右孩子,直到栈空且节点也空。与第一版先序相比,这里在出栈的时候访问。
/**
* 中序非递归遍历
* @param root
*/
public static void inOrderTravalWithStack(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
ArrayList<Integer> res = new ArrayList<>();
TreeNode treeNode = root;
while (treeNode != null || !stack.isEmpty()) {
while (treeNode != null) {
stack.push(treeNode);
treeNode = treeNode.left;
}
if (!stack.isEmpty()) {
treeNode = stack.pop();
res.add(treeNode.data);
treeNode = treeNode.right;
}
}
System.out.println(res);
}
后序非递归遍历
思路:要保证根结点在其左孩子和右孩子访问之后才能访问,因此对于任一结点p,先将其入栈.如果p不存在左孩子和右孩子,则可直接访问;否则将p的右孩子和左孩子依次入栈然后把p的左右孩子结点赋值null,这样就保证了每次取栈顶的元素的时候左孩子在右孩子前面被访问, 左孩子和右孩子都在根结点前面被访问
能得到正确结果,但是变更了节点的左右孩子。。。
/**
* 非递归后序遍历
* 思路:要保证根结点在其左孩子和右孩子访问之后才能访问,因此对于任一结点p,先将其入栈.
* 如果p不存在左孩子和右孩子,则可直接访问;
* 否则将p的右孩子和左孩子依次入栈然后把p的左右孩子结点赋值null,这样就保证了每次取栈顶的元素的时候
* 左孩子在右孩子前面被访问, 左孩子和右孩子都在根结点前面被访问
*/
public static void postOrderWithStack(TreeNode root) {
ArrayList res = new ArrayList();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode tmp = null;
while (!stack.isEmpty()) {
tmp = stack.peek();
if (tmp.left == null && tmp.right == null) {
tmp = stack.pop();
res.add(tmp.data);
} else {
if (tmp.right != null) {
stack.push(tmp.right);
tmp.right = null;
}
if (tmp.left != null) {
stack.push(tmp.left);
tmp.left = null;
}
}
}
System.out.println(res);
}
层序遍历
思路:用队的结构,出一个入俩。
/**
* 层序遍历树
*/
public static void levelOrderTraval(TreeNode root) {
LinkedList<TreeNode> queue = new LinkedList<>();
ArrayList<Integer> res = new ArrayList<>();
queue.offer(root);
TreeNode tmp = null;
while (!queue.isEmpty()) {
tmp = queue.removeFirst();
res.add(tmp.data);
if (tmp.left != null) {
queue.addLast(tmp.left);
}
if (tmp.right != null) {
queue.addLast(tmp.right);
}
}
System.out.println(res);
}
测试代码
package chapter3;
import java.util.*;
/**
* @descrip: 树的各种遍历
* @author: GameryC
* @date: 2020/4/21 12:33
*/
public class TreeAbout {
public static void main(String[] args) {
Integer[] list = new Integer[]{3, 2, 9, null, null, 10, null, null, 8, null, 4};
LinkedList<Integer> inputList = new LinkedList<>(Arrays.asList(list));
TreeNode root = creatBinaryTree(inputList);
System.out.println("先序遍历");
preOrderTraval(root);
preOrderTravalWithStack(root);
preOrderTravalWithStackV2(root);
System.out.println("中序遍历");
inOrderTraval(root);
inOrderTravalWithStack(root);
System.out.println("后序遍历");
postOrder(root);
postOrderWithStack(root);
// 上边后序非递归遍历把root 左右置空了,树结构不对,要重新生成
// 前边生成树的时候把 linkedList 清空了,这次要用新的
root = creatBinaryTree(new LinkedList<>(Arrays.asList(list)));
System.out.println("层序遍历");
levelOrderTraval(root);
}
/**
* 递归先序遍历二叉树
* @param root
*/
public static void preOrderTraval(TreeNode root) {
if (root == null) return;
System.out.println(root);
preOrderTraval(root.left);
preOrderTraval(root.right);
}
/**
* 非递归先序遍历二叉树
* @param root
*/
public static void preOrderTravalWithStack(TreeNode root) {
ArrayList res = new ArrayList();
TreeNode treeNode = root;
Stack<TreeNode> stack = new Stack<>();
while (treeNode != null || !stack.isEmpty()) {
//加入所有左孩子,同时访问该节点
while (treeNode != null) {
stack.push(treeNode);
res.add(treeNode.data);
treeNode = treeNode.left;
}
//如果没有左孩子,弹出栈顶节点,访问右孩子。
if (!stack.isEmpty()) {
treeNode = stack.pop();
treeNode = treeNode.right;
}
}
System.out.println(res);
}
/**
* 先序非递归
* @param root
*/
public static void preOrderTravalWithStackV2(TreeNode root) {
ArrayList res = new ArrayList();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode tmp = null;
while (!stack.isEmpty()) {
tmp = stack.pop();
res.add(tmp.data);
if (tmp.right != null) {
stack.push(tmp.right);
}
if (tmp.left != null) {
stack.push(tmp.left);
}
}
System.out.println(res);
}
/**
* 中序递归遍历
* @param root
*/
public static void inOrderTraval(TreeNode root) {
if (root == null) return;
inOrderTraval(root.left);
System.out.println(root);
inOrderTraval(root.right);
}
/**
* 中序非递归遍历
* @param root
*/
public static void inOrderTravalWithStack(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
ArrayList<Integer> res = new ArrayList<>();
TreeNode treeNode = root;
while (treeNode != null || !stack.isEmpty()) {
while (treeNode != null) {
stack.push(treeNode);
treeNode = treeNode.left;
}
if (!stack.isEmpty()) {
treeNode = stack.pop();
res.add(treeNode.data);
treeNode = treeNode.right;
}
}
System.out.println(res);
}
/**
* 后序递归遍历
*/
public static void postOrder(TreeNode root) {
if (root == null) return;
postOrder(root.left);
postOrder(root.right);
System.out.println(root);
}
/**
* 非递归后序遍历
* 思路:要保证根结点在其左孩子和右孩子访问之后才能访问,因此对于任一结点p,先将其入栈.
* 如果p不存在左孩子和右孩子,则可直接访问;
* 否则将p的右孩子和左孩子依次入栈然后把p的左右孩子结点赋值null,这样就保证了每次取栈顶的元素的时候
* 左孩子在右孩子前面被访问, 左孩子和右孩子都在根结点前面被访问
*/
public static void postOrderWithStack(TreeNode root) {
ArrayList res = new ArrayList();
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
TreeNode tmp = null;
while (!stack.isEmpty()) {
tmp = stack.peek();
if (tmp.left == null && tmp.right == null) {
tmp = stack.pop();
res.add(tmp.data);
} else {
if (tmp.right != null) {
stack.push(tmp.right);
tmp.right = null;
}
if (tmp.left != null) {
stack.push(tmp.left);
tmp.left = null;
}
}
}
System.out.println(res);
}
/**
* 层序遍历树
*/
public static void levelOrderTraval(TreeNode root) {
LinkedList<TreeNode> queue = new LinkedList<>();
ArrayList<Integer> res = new ArrayList<>();
queue.offer(root);
TreeNode tmp = null;
while (!queue.isEmpty()) {
tmp = queue.removeFirst();
res.add(tmp.data);
if (tmp.left != null) {
queue.addLast(tmp.left);
}
if (tmp.right != null) {
queue.addLast(tmp.right);
}
}
System.out.println(res);
}
/**
* 生成二叉树
* @param inputList 链表
* @return 二叉树
*/
public static TreeNode creatBinaryTree(LinkedList<Integer> inputList) {
if (inputList == null || inputList.isEmpty()) return null;
TreeNode node = null;
Integer data = inputList.removeFirst();
if (data != null) {
node = new TreeNode(data);
node.left = creatBinaryTree(inputList);
node.right = creatBinaryTree(inputList);
}
return node;
}
/*
* 二叉树节点
*/
private static class TreeNode {
int data;
TreeNode left;
TreeNode right;
TreeNode(int data) {
this.data = data;
}
@Override
public String toString() {
return "TreeNode{" +
"data=" + data +
'}';
}
}
}