非递归遍历二叉树就是自己使用一个栈去模拟系统栈
前序遍历:
先访问根节点,然后把根节点入栈,遍历左子树,再把左子树入栈,直到左子树为空,就出栈一个元素,然后遍历出栈元素的右子树
栈顶元素出栈表示这个元素的左子树已经遍历完了
public List<Integer> preorderTraversal(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
List<Integer> list = new ArrayList<>();
while (root != null || !stack.isEmpty()) {
if (root != null) {
list.add(root.val); //访问元素
stack.push(root);
root = root.left; //相当于递归里的dfs(root.left)
} else {
//来到这里表面根节点以及左子树都已经访问完了然后访问右子树
//相当于递归里的root为空然后出栈
TreeNode pop = stack.pop(); //相当于走完了递归里的dfs(root.left)
root = pop.right; //相当于递归里的dfs(root.right)
}
}
return list;
}
例如二叉树
1
/
2 3
根节点1加入list,然后1入栈,
| |
| |
|1 | list:1
1的左子树2加入list,1的左子树2入栈,
| |
|2 |
|1 | list:1 2
2的左子树为空,表示2的左子树已经遍历完了然后出栈2,
| |
| |
|1 |
遍历2的右子树,2的右子树为空,表示2的右子树已经遍历完了然后出栈1,
| |
| |
| |
遍历1的右子树3,3加入list,3入栈,
| |
| |
|3 | list:1 2 3
3的左子树为空,表示3的左子树已经遍历完了,3出栈,
| |
| |
| |
遍历3的右子树,3的右子树为空,表示3的右子树已经遍历完了,此时root和栈都为空退出循环
中序遍历:
先把根节点入栈,遍历左子树,左子树入栈,直到左子树为空,就出栈一个元素,然后访问这个元素,然后遍历出栈元素的右子树,右子树入栈,然后再遍历右子树的左子树,左子树入栈,即重复之前的步骤
栈顶元素出栈表示这个元素的左子树已经遍历完了
public List<Integer> inorderTraversal(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
List<Integer> list = new ArrayList<>();
while (root != null || !stack.isEmpty()) {
if (root != null) {
stack.push(root);
root = root.left; //相当于递归里的dfs(root.left)
} else {
//来到这里表面左子树都已经访问完了然后访问右子树 相当于递归里的root为空返回
TreeNode pop = stack.pop();//相当于走完了递归里的dfs(root.left)
list.add(pop.val);//访问元素
root = pop.right; //相当于递归里的dfs(root.right)
}
}
return list;
}
例如二叉树
1
/
2 3
根节点1入栈 root为1
| |
| |
|1 |
遍历1的左子树2,2入栈 root为2
| |
|2 |
|1 |
遍历2的左子树,为空,表示左子树遍历完了,出栈一个2,访问这个元素 root为null
| |
| |
|1 | list: 2
遍历2的右子树,为空,表示右子树遍历完了,出栈一个1,访问这个元素 root为null
| |
| |
| | list: 2 1
遍历1的右子树3,不为空,入栈 root为3
| |
| |
|3 |
遍历3的左子树,为空,表示左子树遍历完了,出栈一个3,访问这个元素 root为null
| |
| |
| | list: 2 1 3
遍历3的右子树,为空,表示右子树遍历完了,此时root和栈都为空退出循环
后序遍历:
先把根节点入栈,遍历左子树,左子树入栈,直到左子树为空,就出栈一个元素,然后判断这个元素的右子树是否为空或者已经被访问了, 是的话就访问这个元素,否的话就遍历出栈元素的右子树,右子树入栈,然后再遍历右子树的左子树,左子树入栈,即重复之前的步骤
栈顶元素出栈表示这个元素的左子树和右子树已经遍历完了
public List<Integer> postorderTraversal(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
List<Integer> list = new ArrayList<>();
TreeNode visited = null;
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left; //相当于递归里的dfs(root.left)
}
TreeNode pop = stack.pop();//相当于走完了递归里的dfs(root.left)
//相当于递归里的root为空然后出栈
if (pop.right == null || pop.right == visited) {//相当于走完了递归里的dfs(root.right)
visited = pop;
root = null;
list.add(pop.val); //访问元素
} else { //右子树还没遍历
stack.push(pop);
root = pop.right; //相当于递归里的dfs(root.right)
}
}
return list;
}
//这段和上面的区别是少了出栈如果出栈元素右子树不为空且还没被访问再重新入栈的步骤
//栈顶元素的右子树为空或被访问了才出栈,否则就栈顶元素的右子树入栈
public List<Integer> postorderTraversal(TreeNode root) {
Deque<TreeNode> stack = new LinkedList<>();
List<Integer> list = new ArrayList<>();
TreeNode visited = null;
while (root != null || !stack.isEmpty()) {
while (root != null) {
stack.push(root);
root = root.left; //相当于递归里的dfs(root.left)
}
TreeNode peek = stack.peek();
if (peek.right == null || peek.right == visited) {
visited = stack.pop();
list.add(peek.val);
} else {
root = peek.right; //相当于递归里的dfs(root.right)
}
}
return list;
}
例如二叉树 1
/
2 3
根节点1入栈 root为1
| |
| |
|1 |
遍历1的左子树2,2入栈 root为2
| |
|2 |
|1 |
遍历2的左子树,为空,表示左子树遍历完了,出栈一个2,然后判断2的右子树是否为空或者已经被访问了 root为null
| |
| |
|1 |
2的右子树为空,就访问2,并把2标记为已访问的,把root置空的目的是出栈一个元素1 root为null
| |
| |
| | list: 2
然后判断1的右子树是否为空或者已经被访问了遍历,1的右子树3,不为空,1重新入栈 root为3
| |
| |
|1 |
root为3入栈,遍历3的左子树,为空,表示左子树遍历完了,出栈一个3,然后判断3的右子树是否为空或者已经被访问了 root为null
| |
|3 |
|1 |
3的右子树为空,就访问3,并把3标记为已访问的,把root置空的目的是出栈一个元素1 root为null
| |
| |
|1 | list:2 3
1的右子树已经被访问了,就访问1,并把1标记为已访问的,把root置空的目的是出栈一个元素1 root为null
| |
| |
| | list:2 3 1
此时root和栈都为空退出循环