二叉树递归遍历
94.二叉树的中序遍历
144.二叉树的前序遍历
145.二叉树的后序遍历
递归解法代码如下:
/*前序遍历*/
public static void preOrderRecur(TreeNode head) {
if (head == null) {
return;
}
System.out.print(head.value + " ");
preOrderRecur(head.left);
preOrderRecur(head.right);
}
/*中序遍历*/
public static void preOrderRecur(TreeNode head) {
if (head == null) {
return;
}
preOrderRecur(head.left);
System.out.print(head.value + " ");
preOrderRecur(head.right);
}
/*后序遍历*/
public static void postOrderRecur(TreeNode head) {
if (head == null) {
return;
}
postOrderRecur(head.left);
postOrderRecur(head.right);
System.out.print(head.value + " ");
}
迭代遍历:
注:递归的实现就是:每一次递归调用都会把函数的局部变量、参数值和返回地址等压入调用栈中,然后递归返回的时候,从栈顶弹出上一次递归的各项参数,所以这就是递归为什么可以返回上一层位置的原因。
所以选择使用Stack栈,由于前序遍历输出为 “中左右” ,所以使用Stack 加入节点顺序便为,先加入右子树,再加入左子树才能够满足前序遍历结果。
完整代码如下:
class Solution {
public List<Integer> preorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
Stack<TreeNode> stack = new Stack<>();
if(root == null){
return res;
}
stack.push(root);
while(!stack.isEmpty()){
TreeNode cur = stack.pop();
res.add(cur.val);
if(cur.right != null){
stack.push(cur.right);
}
if(cur.left != null){
stack.push(cur.left);
}
}
return res;
}
}
中序迭代遍历代码如下:
class Solution {
public List<Integer> inorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
TreeNode cur = root;
while(cur != null || !stack.isEmpty()){
// 直到遍历到左子树最后的左子节点
if(cur != null){
stack.push(cur);
cur = cur.left;
}else{
// 如果当前节点为null,说明已经到达某个叶子节点的左子树底部,需要将栈顶节点弹出,并将其值添加到结果列表中
cur = stack.pop();
res.add(cur.val);
cur = cur.right;
}
}
return res;
}
}
后续迭代遍历:
因为后续遍历是 左 - 右 - 中,前序遍历是 中 - 左 - 右,所以我们只需要稍微改变前序遍历中出入栈顺序。
完整代码如下:
class Solution {
public List<Integer> postorderTraversal(TreeNode root) {
List<Integer> res = new ArrayList<>();
if(root == null){
return res;
}
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while(!stack.isEmpty()){
TreeNode cur = stack.pop();
res.add(cur.val);
// 先存入左节点,出栈时则先弹出右子节点,使遍历顺序为 中 - 右 - 左
if(cur.left != null){
stack.push(cur.left);
}
if(cur.right != null){
stack.push(cur.right);
}
}
Collections.reverse(res);
return res;
}
}
后续迭代遍历按照 左 - 右 - 中 进行迭代遍历:
与中序的不同之处在于:
- 中序遍历中,从栈中弹出的节点,其左子树是访问完了,可以直接访问该节点,然后接下来访问右子树。
- 后序遍历中,从栈中弹出的节点,我们只能确定其左子树肯定访问完了,但是无法确定右子树是否访问过。
因此,我们在后序遍历中,引入了一个prev来记录历史访问记录。
- 当访问完一棵子树的时候,我们用prev指向该节点。
- 这样,在回溯到父节点的时候,我们可以依据prev是指向左子节点,还是右子节点,来判断父节点的访问情况。
class Solution{
public List<Integer> method1(TreeNode root) {
List<Integer> ans=new LinkedList<>();
Stack<TreeNode> stack=new Stack<>();
TreeNode prev=null;
//主要思想:
//由于在某颗子树访问完成以后,接着就要回溯到其父节点去
//因此可以用prev来记录访问历史,在回溯到父节点时,可以由此来判断,上一个访问的节点是否为右子树
while(root!=null||!stack.isEmpty()){
while(root!=null){
stack.push(root);
root=root.left;
}
//从栈中弹出的元素,左子树一定是访问完了的
root=stack.pop();
//现在需要确定的是是否有右子树,或者右子树是否访问过
//如果没有右子树,或者右子树访问完了,也就是上一个访问的节点是右子节点时
//说明可以访问当前节点
if(root.right==null||prev==root.right){
ans.add(root.val);
//更新历史访问记录,这样回溯的时候父节点可以由此判断右子树是否访问完成
prev=root;
root=null;
}else{
//如果右子树没有被访问,那么将当前节点压栈,访问右子树
stack.push(root);
root=root.right;
}
}
return ans;
}
}