文章目录
1 二叉树基本算法
1.1 二叉树的遍历
1.1.1 二叉树节点定义
Class Node{
// 节点的值类型
V value;
// 二叉树的左孩子指针
Node left;
// 二叉树的右孩子指针
Node right;
}
1.1.2 递归实现先序中序后序遍历
先序:任何子树的处理顺序都是,先头结点,再左子树,再右子树。先处理头结点
中序:任何子树的处理顺序都是,先左子树,再头结点,再右子树。中间处理头结点
后序:任何子树的处理顺序都是,先左子树,再右子树,再头结点。最后处理头结点
对于下面的一棵树:
graph TD
1-->2
1-->3
2-->4
2-->5
3-->6
3-->7
1、 先序遍历为:1 2 4 5 3 6 7
2、 中序遍历为:4 2 5 1 6 3 7
3、 后序遍历为:4 5 2 6 7 3 1
package class07;
public class Code01_RecursiveTraversalBT {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int v) {
value = v;
}
}
public static void f(Node head) {
if (head == null) {
return;
}
// 1 此处打印等于先序
f(head.left);
// 2 此处打印等于中序
f(head.right);
// 3 此处打印等于后序
}
// 先序打印所有节点
public static void pre(Node head) {
if (head == null) {
return;
}
// 打印头
System.out.println(head.value);
// 递归打印左子树
pre(head.left);
// 递归打印右子树
pre(head.right);
}
// 中序遍历
public static void in(Node head) {
if (head == null) {
return;
}
in(head.left);
System.out.println(head.value);
in(head.right);
}
// 后序遍历
public static void pos(Node head) {
if (head == null) {
return;
}
pos(head.left);
pos(head.right);
System.out.println(head.value);
}
public static void main(String[] args) {
Node head = new Node(1);
head.left = new Node(2);
head.right = new Node(3);
head.left.left = new Node(4);
head.left.right = new Node(5);
head.right.left = new Node(6);
head.right.right = new Node(7);
pre(head);
System.out.println("========");
in(head);
System.out.println("========");
pos(head);
System.out.println("========");
}
}
结论:对于树的递归,每个节点实质上会到达三次,例如上文的树结构,对于f函数,我们传入头结点,再调用左树再调用右树。实质上经过的路径为1 2 4 4 4 2 5 5 5 2 1 3 6 6 6 3 7 7 7 3 1。我们在每个节点三次返回的基础上,第一次到达该节点就打印,就是先序,第二次到达该节点打印就是中序,第三次到达该节点就是后序。
所以先序中序后序,只是我们的递归顺序加工出来的结果!
1.1.3 非递归实现先序中序后序遍历(DFS)
思路:由于任何递归可以改为非递归,我们可以使用压栈来实现,实质就是深度优先遍历(DFS)。用先序实现的步骤,其他类似:
步骤一,把节点压入栈中,弹出就打印
步骤二,如果有右孩子先压入右孩子
步骤三,如果有左孩子压入左孩子
package class07;
import java.util.Stack;
public class Code02_UnRecursiveTraversalBT {
public static class Node {
public int value;
public Node left;
public Node right;
public Node(int v) {
value = v;
}
}
// 非递归先序
public static void pre(Node head) {
System.out.print("pre-order: ");
if (head != null) {
Stack<Node> stack = new Stack<Node>();
stack.add(head);
while (!stack.isEmpty()) {
// 弹出就打印
head = stack.pop();
System.out.print(head.value + " ");
// 右孩子不为空,压右
if (head.right != null) {
stack.push(head.right) ;
}
// 左孩子不为空,压左
if (head.left != null) {
stack.push(head.left);
}
}
}
System.out.println();
}
// 非递归中序
public static void in(Node head) {
System.out.print("in-order: ");
if (head != null) {
Stack<Node> stack = new Stack<Node>();
while (!stack.isEmpty() || head != null) {
// 整条左边界依次入栈
if (head != null) {
stack.push(head);
head = head.left;
// 左边界到头弹出一个打印,来到该节点右节点,再把该节点的左树以此进栈
} else {
head = stack.pop();
System.out.print(head.value + " ");
head = head.right;
}
}
}
System.out.println();
}
// 非递归后序
public static void pos1(Node head) {
System.out.print("pos-order: ");
if (head != null) {
Stack<Node> s1 = new Stack<Node>();
// 辅助栈
Stack<Node> s2 = new Stack<Node>();
s1.push(head);
while (!s1.isEmpty()) {
head = s1.pop();
s2.push(head);
if (head.left != null) {
s1.push(head.left);
}
if (head.right != null) {
s1.push(head.right);
}
}
while (!s2.isEmpty()) {
System.out.print(s2.pop().value + " ");
}
}
System.out.println();
}
// 非递归后序2:用一个栈实现后序遍历,比较有技巧
public static void pos2(Node h) {
System.out.print("pos-order: ");
if (h != null) {
Stack<Node> stack = new Stack<Node>();
stack.push(h);
Node c = null;
while (!stack.isEmpty()) {
c = stack.peek();
//当做左树有没有处理的逻辑来看
if (c.left != null && h != c.left && h != c.right) {
stack.push(c.left);
//当做右树有没有处理的逻辑来看
} else if (c.right != null && h !&#