二叉树的遍历
定义结点
@Data // lombok
public class Node {
int value; // 权
Node leftNode; // 左孩子
Node rightNode; // 右孩子
public Node(int value) {
this.value = value;
}
public Node(int value, Node leftNode, Node rightNode) {
this.value = value;
this.leftNode = leftNode;
this.rightNode = rightNode;
}
}
定义树
Node node8 = new Node(8);
Node node7 = new Node(7);
Node node6 = new Node(6);
Node node5 = new Node(5, node7, node8);
Node node4 = new Node(4);
Node node3 = new Node(3, null, node6);
Node node2 = new Node(2, node4, node5);
Node node1 = new Node(1, node2, node3);
深度优先遍历
前序遍历(递归和非递归)
递归
public static void beforeSearch(Node node) {
if (node != null) {
System.out.print(node.value + " ");
beforeSearch(node.leftNode);
beforeSearch(node.rightNode);
}
}
递归的方法很容易理解,这里就不做过多解释
非递归
根据前序遍历的顺序,先访问根结点,然后再访问左子树和右子树。所以,对于任意结点node,直接输出权值即可,之后在判断左子树是否为空,不为空时即重复上面的步骤,直到其为空。若为空,则需要访问右子树。注意,在访问过左孩子之后,需要反过来访问其右孩子,所以,需要栈这种数据结构的支持。对于任意一个结点node,具体步骤如下:
(a)访问之,并把结点node入栈,当前结点置为左孩子;
(b)判断结点node是否为空,若为空,则取出栈顶结点并出栈,将右孩子置为当前结点;否则重复a)步直到当前结点为空或者栈为空(可以发现栈中的结点就是为了访问右孩子才存储的)
// 非递归前序遍历
// 遍历顺序中左右
public static void before(Node node) {
LinkedList<Node> list1 = new LinkedList<>();
// 存储祖先结点
Node newNode = node;
// 结束的条件为所有的结点都被访问
while (newNode != null || !list1.isEmpty()) {
// 如果孩子节点不为空则输出节点值并且访问其左子树并令节点入栈方便访问其右子树
if (newNode != null) {
System.out.print(newNode.value + " ");
list1.push(newNode);
newNode = newNode.leftNode;
} else { // 如果孩子节点为空则访问父亲节点的右子树
Node tempNode = list1.pop();
newNode = tempNode.rightNode;
}
}
}
中序遍历
递归
// 中序遍历递归
public static void middleSearch(Node node) {
if (node != null) {
middleSearch(node.leftNode);
System.out.print(node.value + " ");
middleSearch(node.rightNode);
}
}
非递归
和前序遍历思路基本差不多
// 非递归中序遍历
// 遍历顺序左中右
public static void middle(Node node) {
LinkedList<Node> list2 = new LinkedList<>();
Node newNode = node;
while (newNode != null || !list2.isEmpty()) {
if (newNode != null) {
list2.push(newNode);
newNode = newNode.leftNode;
} else {
Node tempNode = list2.pop();
System.out.print(tempNode.value + " ");
newNode = tempNode.rightNode;
}
}
}
后序遍历
递归
// 后序遍历递归
public static void afterSearch(Node node) {
if (node != null) {
afterSearch(node.leftNode);
afterSearch(node.rightNode);
System.out.print(node.value + " ");
}
}
非递归
非递归的方法后序遍历稍微有点麻烦,从后序遍历与先序遍历的关系中我们可以知道逆后序遍历为先序遍历交换左右子树的遍历顺序得到的,所以需要两个栈,第一个栈用来存储先序遍历交换左右子树的遍历的中介结果,第二个是存储后序遍历的结果(逆序也就是可以理解为先进后出的意思)
// 非递归后序遍历
// 遍历顺序左右中
// 可以投机先序遍历逆转就是后序遍历
public static void after(Node node) {
LinkedList<Node> list3 = new LinkedList<>();
LinkedList<Node> list4 = new LinkedList<>();
if (node != null) {
list3.push(node);
}
while (!list3.isEmpty()) {
Node newNode = list3.pop();
list4.push(newNode);
if (newNode.leftNode != null) {
list3.push(newNode.leftNode);
}
if (newNode.rightNode != null) {
list3.push(newNode.rightNode);
}
}
while (!list4.isEmpty()) {
System.out.print(list4.pop().value + " ");
}
}
广度优先遍历
层次遍历
public static void level(Node node) {
Queue<Node> queue = new LinkedList<>();
queue.add(node);
while (!queue.isEmpty()) {
Node newNode = queue.poll();
System.out.print(newNode.value + " ");
if (newNode.leftNode != null) {
queue.add(newNode.leftNode);
}
if (newNode.rightNode != null) {
queue.add(newNode.rightNode);
}
}
}