「数据结构与算法」树的遍历
树
class TreeNode {
int val;
// 左子树
TreeNode left;
// 右子树
TreeNode right;
// 构造方法
TreeNode(int x) {
val = x;
}
}
先序遍历
递归先序遍历:
// 递归先序遍历
void recursionPreOrderTraversal(TreeNode root) {
if (root != null) {
// 先序
System.out.print(root.val + " ");
recursionPreOrderTraversal(root.left);
recursionPreOrderTraversal(root.right);
}
}
在遍历完节点的左子树后接着遍历节点的右子树,为了能找到该节点,需要使用栈来进行暂存。
非递归先序遍历:
// 非递归先序遍历
void preOrderTraversal(TreeNode root) {
// 该栈用于暂存节点
Stack<TreeNode> treeNodeStack = new Stack<TreeNode>();
// 游标
TreeNode node = root;
// 最后一个节点时,左右子树为空,并且栈也为空
while (node != null || !treeNodeStack.isEmpty()) {
while (node != null) {
// 先序访问
System.out.print(node.val + " ");
treeNodeStack.push(node);
node = node.left;
}
// 此时左子树为空,考虑右子树
// 如果栈已空,无需考虑
if (!treeNodeStack.isEmpty()) {
// 弹出栈顶元素
node = treeNodeStack.pop();
node = node.right;
}
}
}
中序遍历
递归中序遍历:
// 递归中序遍历
void recursionInOrderTraversal(TreeNode root) {
if (root != null) {
recursionInOrderTraversal(root.left);
// 中序
System.out.print(root.val + " ");
recursionInOrderTraversal(root.right);
}
}
在遍历完节点的左子树后接着遍历节点的右子树,为了能找到该节点,需要使用栈来进行暂存。
非递归中序遍历:
// 非递归中序遍历
void inOrderTraversal(TreeNode root) {
// 该栈用于暂存节点
Stack<TreeNode> treeNodeStack = new Stack<TreeNode>();
// 游标
TreeNode node = root;
// 最后一个节点时,左右子树为空,并且栈也为空
while (node != null || !treeNodeStack.isEmpty()) {
while (node != null) {
treeNodeStack.push(node);
node = node.left;
}
// 此时左子树为空,考虑右子树
// 如果栈已空,无需考虑
if (!treeNodeStack.isEmpty()) {
// 弹出栈顶元素
node = treeNodeStack.pop();
// 中序访问
System.out.print(node.val + " ");
node = node.right;
}
}
}
后序遍历
递归后序遍历
// 递归后序遍历
void recursionPostOrderTraversal(TreeNode root) {
if (root != null) {
recursionPostOrderTraversal(root.left);
recursionPostOrderTraversal(root.right);
// 后序
System.out.print(root.val + " ");
}
}
非递归后序遍历:
// 非递归后序遍历
void postOrderTraversal(TreeNode root) {
// 该栈用于暂存节点
Stack<TreeNode> treeNodeStack = new Stack<TreeNode>();
// 游标
TreeNode node = root;
// 保存最后访问节点
TreeNode lastVisit = root;
// 最后一个节点时,左右子树为空,并且栈也为空
while (node != null || !treeNodeStack.isEmpty()) {
while (node != null) {
treeNodeStack.push(node);
node = node.left;
}
// 查看当前栈顶元素
node = treeNodeStack.peek();
// 如果其右子树也为空,或者右子树已经访问
// 则可以直接输出当前节点的值
if (node.right == null || node.right == lastVisit) {
// 后序访问
System.out.print(node.val + " ");
treeNodeStack.pop();
lastVisit = node;
node = null;
} else {
//否则,继续遍历右子树
node = node.right;
}
}
}
前序遍历之所以最简单,是因为遍历过程中最先遇到的根节点是最先访问的,而在后序遍历中,最先遇到的根节点是最后访问的。后序遍历的顺序是“左-右-根”,如果能倒序一下成为“根-右-左”,那么遍历的整体难度就会下降。同时,考虑到栈本身是天然的倒序工具,所以我们可以考虑用一个栈将输出顺序反过来即可。这就引出了双栈法,简单来说其思路是:
- 用一个栈实现“根-右-左”的遍历顺序
- 用一个将“根-右-左”倒序成“左-右-根”
双栈法后序遍历:
// 双栈法后序遍历
void preOrderTraversal(TreeNode root) {
// 该栈用于暂存节点
Stack<TreeNode> treeNodeStack = new Stack<TreeNode>();
// 该栈用于倒序
Stack<TreeNode> reverseStack = new Stack<>();
// 游标
TreeNode node = root;
// 最后一个节点时,左右子树为空,并且栈也为空
while (node != null || !treeNodeStack.isEmpty()) {
while (node != null) {
// 先序访问
reverseStack.push(node);
treeNodeStack.push(node);
node = node.right;
}
// 此时左子树为空,考虑右子树
// 如果栈已空,无需考虑
if (!treeNodeStack.isEmpty()) {
// 弹出栈顶元素
node = treeNodeStack.pop();
node = node.left;
}
}
while (!reverseStack.isEmpty()) {
System.out.print(reverseStack.pop().val + " ");
}
}
参考
https://www.jianshu.com/p/456...
https://zhuanlan.zhihu.com/p/...
https://segmentfault.com/a/11...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。