2 使用栈实现二叉树的前中后序遍历
2.1 前序遍历(144.二叉树的前序遍历)
2.1.1 解题思路
二叉树的前序遍历即先遍历根节点、再遍历左节点、最后遍历右节点。
- 先把每一个根节点加入栈,弹出放入遍历集合中。
- 找到当前结点的右节点,入栈操作。
- 再找到当前结点的左节点,入栈操作。
重复以上操作,直至栈为空,证明所有元素已被遍历完。
注意:
前序遍历的重点,就是先让右节点入栈、再让左节点入栈。
这样基于栈 先进后出的特点,会实现出栈顺序为:根节点——左节点——右节点。
2.1.2 Java实现(栈——迭代)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
List<Integer> res = new ArrayList<>();
stack.push(root);
while(!stack.empty()){
TreeNode node = stack.pop(); //中
if(node!= null){
res.add(node.val);
}else{
continue;
}
stack.push(node.right); //左
stack.push(node.left); //右
}
return res;
}
}
2.1.3 Java实现(递归)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
preorder(root,res);
return res;
}
public void preorder(TreeNode root,List<Integer> res){
if (root == null) {
return;
}
res.add(root.val);
preorder(root.left,res);
preorder(root.right,res);
}
}
2.2 中序遍历(94. 二叉树的中序遍历)
2.2.1 解题思路
二叉树的中序遍历与前序遍历差别较大。
主要原因在于:
- 前序遍历中,从root节点开始,访问顺序与遍历顺序一致。因此直接按照访问顺序处理即可。
- 但中序遍历中,先访问的是root节点,但是遍历顺序是从左节点先开始。
- 因此从root节点开始,先一层一层向下访问,将左节点全部入栈。直至左子树的最后一层。
- 此时依次出栈(即遍历结果——加入list集合中),并访问右节点。
基于栈 先进后出的原理,此处入栈的顺序为:根节点——左节点,出栈之后再将右节点入栈。因此,出栈后的顺序即为左中右。
代码中保证了,有左节点先遍历左节点,没有左节点,则遍历根节点。
遍历完根节点才让当前节点的右节点入栈。
2.2.2 Java实现(栈——迭代)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
TreeNode cur = root;
Stack<TreeNode> stack = new Stack<>();
while(!stack.empty() || cur!=null){
if(cur!=null){
stack.push(cur);
cur = cur.left; //左
}else{
cur = stack.pop();
res.add(cur.val); //中
cur = cur.right; //右
}
}
return res;
}
}
2.2.3 Java实现(递归)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
inorder(root,res);
return res;
}
public void inorder(TreeNode root,List<Integer> res){
if (root == null) {
return;
}
inorder(root.left,res);
res.add(root.val);
inorder(root.right,res);
}
}
2.3 后序遍历(145. 二叉树的后序遍历)
2.3.1 解题思路
2.3.2 Java实现(栈——迭代)
解法一:
前序遍历的顺序是 中左右 。后序遍历的顺序是左右中。
则参考前序遍历的递归解法,先遍历为中右左(只需使左节点先入栈,右节点后入栈即可)。
再将中右左的结果反转一下。就可以得到 左右中的遍历顺序。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
List<Integer> res = new ArrayList<>();
while(!stack.empty()){
TreeNode node = stack.pop();
if(node!=null){
res.add(node.val);
}else{
continue;
}
stack.push(node.left);
stack.push(node.right);
}
Collections.reverse(res);
return res;
}
}
解法二:
- 首先将根节点入栈
- 再把根节点的左子树入栈,一层一层遍历,直到左子树的底层
- 得到栈顶元素的值,先不访问,判断栈顶元素是否存在右节点,如果存在并且没有被访问,则将右节点入栈,否则,就访问栈顶元素
需要注意的点就在于需要一个前驱指针,用于判断是否该节点的右节点被访问过。
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
Stack<TreeNode> stack = new Stack<>();
List<Integer> res = new ArrayList<>();
TreeNode cur = root;
TreeNode prev = null;
while(cur!=null || !stack.empty()){
while(cur!=null){
stack.push(cur);
cur = cur.left;
}
cur = stack.pop();
if(cur.right==null || cur.right==prev){
res.add(cur.val);
prev = cur;
cur = null;
}else{
stack.push(cur);
cur = cur.right;
}
}
return res;
}
}
2.3.3 Java实现(递归)
/**
* Definition for a binary tree node.
* public class TreeNode {
* int val;
* TreeNode left;
* TreeNode right;
* TreeNode() {}
* TreeNode(int val) { this.val = val; }
* TreeNode(int val, TreeNode left, TreeNode right) {
* this.val = val;
* this.left = left;
* this.right = right;
* }
* }
*/
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
postorder(root,res);
return res;
}
public void postorder(TreeNode root,List<Integer> res){
if (root == null) {
return;
}
postorder(root.left,res);
postorder(root.right,res);
res.add(root.val);
}
}
2.4 迭代法实现前序、中序、后序遍历的统一解法(重要)
2.4.1 中序遍历
使用栈的话,「无法同时解决访问节点(遍历节点)和处理节点(将元素放进结果集)不一致的情况」。
「那我们就将访问的节点放入栈中,把要处理的节点也放入栈中但是要做标记。」
如何标记呢,「就是要处理的节点放入栈之后,紧接着放入一个空指针作为标记。」 这种方法也可以叫做标记法。
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root!=null){stack.push(root);}
while(!stack.empty()){
TreeNode node = stack.peek();
if(node!=null){
stack.pop();
if(node.right!=null){stack.push(node.right);};
stack.push(node);
stack.push(null);
if(node.left!=null){stack.push(node.left);}
}else{
stack.pop();
node = stack.pop();
res.add(node.val);
}
}
return res;
}
}
2.4.2 前序遍历
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root!=null){stack.push(root);}
while(!stack.empty()){
TreeNode node = stack.peek();
if(node!=null){
stack.pop();
if(node.right!=null){stack.push(node.right);};
if(node.left!=null){stack.push(node.left);}
stack.push(node);
stack.push(null);
}else{
stack.pop();
node = stack.pop();
res.add(node.val);
}
}
return res;
}
}
2.4.3 后序遍历
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root!=null){stack.push(root);}
while(!stack.empty()){
TreeNode node = stack.peek();
if(node!=null){
stack.pop();
stack.push(node);
stack.push(null);
if(node.right!=null){stack.push(node.right);}
if(node.left!=null){stack.push(node.left);}
}else{
stack.pop();
node = stack.pop();
res.add(node.val);
}
}
return res;
}
}