几种遍历规则
递归遍历和非递归遍历实现
递归遍历实现
原理也比较简单,直接看代码既可
public class Node {
private int value;
private Node leftNode;
private Node rightNode;
public Node(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeftNode() {
return leftNode;
}
public void setLeftNode(Node leftNode) {
this.leftNode = leftNode;
}
public Node getRightNode() {
return rightNode;
}
public void setRightNode(Node rightNode) {
this.rightNode = rightNode;
}
}
public class BinaryTree {
// 前序
public static void beforeTheOrder(Node node) {
if (node == null) {
return;
}
System.out.println(node.getValue());
beforeTheOrder(node.getLeftNode());
beforeTheOrder(node.getRightNode());
}
// 中序
public static void middleTheOrder(Node node) {
if (node == null) {
return;
}
middleTheOrder(node.getLeftNode());
System.out.println(node.getValue());
middleTheOrder(node.getRightNode());
}
// 后序
public static void afterTheOrder(Node node) {
if (node == null) {
return;
}
afterTheOrder(node.getLeftNode());
afterTheOrder(node.getRightNode());
System.out.println(node.getValue());
}
/**
1
/ \
2 3
/ \ / \
4 5 6 7
\ /
8 9
*/
public static class BinaryTreeBuilder {
public static Node bulid() {
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
Node n4 = new Node(4);
Node n5 = new Node(5);
Node n6 = new Node(6);
Node n7 = new Node(7);
Node n8 = new Node(8);
Node n9 = new Node(9);
n1.setLeftNode(n2);
n1.setRightNode(n3);
n2.setLeftNode(n4);
n2.setRightNode(n5);
n3.setLeftNode(n6);
n3.setRightNode(n7);
n4.setRightNode(n8);
n7.setLeftNode(n9);
return n1;
}
}
}
测试
public class BinaryTreeTest {
public static void main(String[] args) {
Node root = BinaryTree.BinaryTreeBuilder.bulid();
System.out.println("前序------------");
BinaryTree.beforeTheOrder(root);
System.out.println("中序------------");
BinaryTree.middleTheOrder(root);
System.out.println("后序------------");
BinaryTree.afterTheOrder(root);
System.out.println("------------");
}
}
测试结果
二叉树结构
1
/ \
2 3
/ \ / \
4 5 6 7
\ /
8 9
前序------------
1
2
4
8
5
3
6
7
9
中序------------
4
8
2
5
1
6
3
9
7
后序------------
8
4
5
2
6
9
7
3
1
------------
非递归遍历实现
前序遍历非递归实现
前序遍历思路,借助栈Stack实现。设初始化的时,将根结点放入栈中。
- 后续操作,只要将栈中元素出栈,输出当前元素
- 若子节点不为空,依次将右子节点、左子节点入栈
- 重复步骤1
对应代码实现
public static void beforeTheOrder(Node node) {
Stack<Node> stack = new Stack<>();
stack.add(node);
while (!stack.isEmpty()) {
Node top = stack.pop();
// 前序遍历,根节点优先输出
System.out.println(top.getValue());
Node rightNode = top.getRightNode();
if (rightNode != null) {
stack.push(rightNode);
}
Node leftNode = top.getLeftNode();
if (leftNode != null) {
stack.push(leftNode);
}
}
}
中序遍历非递归实现
中序遍历思路,由于中序遍历根节点的有两种逻辑,我们只要通过变量区分此时根节点处于哪一种状态即可。
- 定义A节点状态为0时,执行A->B逻辑
- 定义A节点状态为1时,执行A->C逻辑
借助栈Stack实现,设初始化的时,将根结点放入栈中。
- 开始:从栈顶顶取出结点,假设当前顶部结点为A
- A节点状态为0
- 判断当前A节点是否含有左节点,若有,入栈
- 设置A节点状态为1
- 从开始处重新执行
- A节点状态为1
- 输出当前节点
- 判断当前节点是否含有右节点,若有,入栈
- 从开始处重新执行
对应代码实现
public class Node {
private int value;
private Node leftNode;
private Node rightNode;
/** 1:已访问,0:未访问 */
private int flag;
public Node(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
public Node getLeftNode() {
return leftNode;
}
public void setLeftNode(Node leftNode) {
this.leftNode = leftNode;
}
public Node getRightNode() {
return rightNode;
}
public void setRightNode(Node rightNode) {
this.rightNode = rightNode;
}
public int getFlag() {
return flag;
}
public void setFlag(int flag) {
this.flag = flag;
}
}
public static void middleTheOrder(Node node) {
Stack<Node> stack = new Stack<>();
stack.add(node);
while (!stack.isEmpty()) {
Node peek = stack.peek();
int flag = peek.getFlag();
if (flag == 0) {
// 本次访问
peek.setFlag(1);
Node leftNode = peek.getLeftNode();
if (leftNode != null) {
stack.add(leftNode);
}
} else if (flag == 1) {
Node popNode = stack.pop();
System.out.println(popNode.getValue());
Node rightNode = popNode.getRightNode();
if (rightNode != null) {
stack.add(rightNode);
}
}
}
}
后序遍历非递归实现
后续遍历思路和中序遍历思路一致,多一种状态而已,这里再分析,直接给出代码实现
public static void afterTheOrder(Node node) {
Stack<Node> stack = new Stack<>();
stack.add(node);
while (!stack.isEmpty()) {
Node peek = stack.peek();
int flag = peek.getFlag();
if (flag == 0) {
// 本次访问
peek.setFlag(1);
Node leftNode = peek.getLeftNode();
if (leftNode != null) {
stack.add(leftNode);
}
} else if (flag == 1) {
peek.setFlag(2);
Node rightNode = peek.getRightNode();
if (rightNode != null) {
stack.add(rightNode);
}
} else if (flag == 2) {
Node popNode = stack.pop();
System.out.println(popNode.getValue());
}
}
}
层次遍历
广度遍历,直接使用Queue队列即可实现
public static void traversal(Node node) {
Queue<Node> queue = new LinkedBlockingDeque<>();
queue.add(node);
while (!queue.isEmpty()) {
Node pollNode = queue.poll();
System.out.println(pollNode.getValue());
Node leftNode = pollNode.getLeftNode();
if (leftNode != null) {
queue.add(leftNode);
}
Node rightNode = pollNode.getRightNode();
if (rightNode != null) {
queue.add(rightNode);
}
}
}
已知两种遍历顺序,画图
两种方式都是先找出根节点,再确定左节点,右节点
前序:GDAFEMHZ
中序:ADEFGHMZ
- 根据前序规则(GDAFEMHZ),我们可以确定根节点一定是G
- 根据中序遍历(ADEF
G
HMZ),可以根据根节点G左子树有ADEF节点,子树节点有HMZ节点 - 【左子树的确定】根据左子树有ADEF节点,我们去前序中查找,最先出现的一定是左子树有ADEF的根节点,根节点为D,递归,重复步骤二,查找剩下的结构
- 【右子树的确定】根据右子树有HMZ节点,我们去前序中查找,最先出现的一定是右子树有HMZ的根节点,根节点为M,递归,重复步骤二,查找剩下的结构
上述经过第一层,我们可以确定
G
/ \
D M
D、M的子节点确认与G的子树确认过程一致
中序遍历: ADEFGHMZ
后序遍历: AEFDHZMG
这里的关键是后续遍历,后续遍历可以确定根节点G,推理和上述相同,不同的是通过前序还是后续来确定根节点