深度优先遍历
简单来说就是:先序遍历,一棵树从根节点开始,从左往下一直左孩子遍历下去,遍历完之后就从最底下的这个结点依次往上找他们的右节点,直到结束
举个例子,我们有下面这样一棵树
然后我们进行如下的操作进行深度优先遍历
1、我们从根节点1开始遍历,它相邻的节点有2, 3, 4,先遍历节点2,再遍历2的子节点5,然后再遍历5的子节点9。
2、.上图中一 条路已经走到底了(9是叶子节点,再无可遍历的节点),此时就从9回退到上一个节点5,看下节点5是否还有除9以外的节点,没有继续回退到2, 2也没有除5以外的节点,回退到1, 1有除2以外的节点3,所以从节点3开始进行深度优先遍历,如下:
3、同理从10开始往上回溯到6, 6没有除10以外的子节点,再往上回溯,发现3有除6以外的子点7,所以此时会遍历7。
4、从7往上回溯到3,1,发现1还有节点4未遍历,所以此时沿着4,8进行遍历,这样就遍历完成了。
完整的节点的遍历顺序如下(节点上的的蓝色数字代表):
递归代码实现
public class Solution {
private static class Node {
/**
* 节点值
*/
public int value;
/**
* 左节点
*/
public Node left;
/**
* 右节点
*/
public Node right;
public Node(int value, Node left, Node right) {
this.value = value;
this.left = left;
this.right = right;
}
}
public static void dfs(Node treeNode) {
if (treeNode == null) {
return;
}
// 遍历节点
process(treeNode)
// 遍历左节点
dfs(treeNode.left);
// 遍历右节点
dfs(treeNode.right);
}
}
为了防止栈溢出,我们可以使用压栈出栈的方式来实现,因为栈是先进后出嘛,所以我们每次遍历到除了根节点之外的节点,我们先将这个节点的右孩子压栈,再将左孩子压栈,这样子取的时候还是先序遍历的,然后我们每一次都递归实现,保证遍历到所有的结点,这样子可以很好的防止在树的深度过深的情况下造成的栈溢出。
示意图
代码实现
/**
* 使用栈来实现 dfs
* @param root
*/
public static void dfsWithStack(Node root) {
if (root == null) {
return;
}
Stack<Node> stack = new Stack<>();
// 先把根节点压栈
stack.push(root);
while (!stack.isEmpty()) {
Node treeNode = stack.pop();
// 遍历节点
process(treeNode)
// 先压右节点
if (treeNode.right != null) {
stack.push(treeNode.right);
}
// 再压左节点
if (treeNode.left != null) {
stack.push(treeNode.left);
}
}
}
广度优先遍历
也叫层序遍历,通俗点就是一层一层的从左到右遍历
广度优先遍历动图如下,每个节点的值即为它们的遍历顺序。所以广度优先遍历也叫层序遍历,先遍历第一层(节点 1),再遍历第二层(节点 2,3,4),第三层(5,6,7,8),第四层(9,10)。
其实用的是队列
**
* 使用队列实现 bfs
* @param root
*/
private static void bfs(Node root) {
if (root == null) {
return;
}
Queue<Node> stack = new LinkedList<>();
stack.add(root);
while (!stack.isEmpty()) {
Node node = stack.poll();
System.out.println("value = " + node.value);
Node left = node.left;
if (left != null) {
stack.add(left);
}
Node right = node.right;
if (right != null) {
stack.add(right);
}
}
}