【JAVA】二叉树(part3练习版)


二叉树

你会看见眼前的光明!

该文章主要是关于 【二叉树】的练习题。


只在意你在意的,只热爱你热爱的!

1. 检查两颗树是否相同。

相同的树

  1. 思路:

相同树即:结构+值相同:
false两种情况–一个空一个非空;两个非空但是值不相等
其实就相当于前序遍历,然后:根结点、左子树、右子树都相同才是树相同
注意return以及顺序
时间复杂度:O(min(m,n))–最坏情况下,深度=结点数,且最差只需要遍历最小的结点数就行

  1. 代码:
public boolean isSameTree(TreeNode p, TreeNode q) {
        if((p==null&&q!=null) || (p!=null&&q==null)) {
            return false;
        }
        // 来到这儿:要么都不为null 要么都为null
        // 注意:判断节点是不是相同:位置+值!!

        /*if(p!=q) {
            return false;
        }
        // 来到这儿:p==q
        if(p!=null && q!=null) {
            // 进行左右子树的递归判断
            isSameTree(p.left,q.left);
            isSameTree(p.right,q.right);
        }
        return true;*/

        // 修改:
        if(p==null && q==null) {
            return true;
        }
        // 来到这儿:均不为空,判断相等
        if(p.val!= q.val) {
            return false;
        } 
        /*else {
            isSameTree(p.left,q.left);
            isSameTree(p.right,q.right);
        }
        return true;*/
        
        // 注意实际返回形式:
        return (isSameTree(p.left,q.left) && isSameTree(p.right,q.right));
    }
  1. 补充:
    前序遍历:深度优先搜索
    思考:中序遍历、后序遍历是不是深度优先搜索?
    层序遍历:广度优先搜索(与根结点相邻的节点依次遍历)

2. 另一颗树的子树。

另一棵树的子树

  1. 思路:

判断两棵树是否相同,如果不同就再判断是不是左右子树
(会使用到 T1 的判断:树是否相同)
同样是使用递归:首先要进行判空,否则在子树判断时会存在空指针异常
时间复杂度:O(root的节点个数n * subRoot节点个数m)--每个结点都去匹配

  1. 代码:
class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        if((p==null&&q!=null) || (p!=null&&q==null)) {
            return false;
        }
        if(p==null && q==null) {
            return true;
        }
        // 来到这儿:均不为空,判断相等
        if(p.val!= q.val) {
            return false;
        }

        // 注意实际返回形式:
        return (isSameTree(p.left,q.left) && isSameTree(p.right,q.right));
    }

    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        // 注意看下面的修改:
        if(root==null) {
            return false;
        }
        // 判空后判断
        if(isSameTree(root,subRoot)) {
            return true;
        }
        // 如果不相等,下一个节点去对子树根节点  如果对上就可以进入isSameTree判断
        if(isSubtree(root.left,subRoot)) {
            //isSameTree(root.left,subRoot);
            // 注意返回值!!!
            return true;
        }
        if(isSubtree(root.right,subRoot)) {
            //isSameTree(root.right,subRoot);
            return true;
        }
        // 来到这:都不满足
        return false;  
    }
}

3. 二叉树最大深度。

二叉树的最大深度

  1. 思路:

时间复杂度:O(n)
要注意递归不在三目运算符中! 否则会加大运算复杂度,可能会超时!

  1. 代码:
 public int maxDepth(TreeNode root) {
        // 其实就相当于层序遍历并记录:注意递归不在三目运算符中
        // 如何判断一层遍历结束
        if(root==null) {
            return 0;
        }
        // 返回是要判断左子树深还是右子树深
        int leftTree = maxDepth(root.left);
        int rightTree = maxDepth(root.right);
        return (leftTree>rightTree? (leftTree+1):(rightTree+1));
    }

4. 判断一颗二叉树是否是平衡二叉树。

平衡二叉树

  1. 思路:

每个结点左右子树高度差的绝对值不超过1(注意:是要判断每一个结点! 即:每一个结点的左右子树高度差不超过1才能保证平衡二叉树)
平衡:root左子树高度-root右子树高度<=1 && root左右子树都平衡(递归实现)套用求高度的代码! + 判断是否为空树
此时最大深度时间复杂度:O(N^2) --每个结点都会被遍历重复次(上一个结点以及自身结点)

求高度都要进行遍历其下面的节点,解决方案:从下往上返回计算时顺便判断 判断平衡就可以达到O(N)的时间复杂度,但是注意判断过程中还需要再来一个>=0的判断**-- 字节考过**
即:最大深度方法的调用+判断

  1. 代码:

① 时间复杂度:O(n^2)

 // 二叉树的最大深度:
    public int maxDepth(TreeNode root) {
        // 其实就相当于层序遍历并记录:注意递归不在三目运算符中
        // 如何判断一层遍历结束
        if(root==null) {
            return 0;
        }
        // 返回是要判断左子树深还是右子树深
        int leftTree = maxDepth(root.left);
        int rightTree = maxDepth(root.right);
        return (leftTree>rightTree? (leftTree+1):(rightTree+1));
    }

    // 判断平衡二叉树
    public boolean isBalanced(TreeNode root) {
        if(root==null) {
            return true;
        }
        // 返回是要判断左子树深还是右子树深
        int leftTree = maxDepth(root.left);
        int rightTree = maxDepth(root.right);

        // 为什么绝对值要小于等于1:平衡树的定义--每个结点的左右子树(子结点)高度相差<=1
        // 平衡:每个结点的左右子树相差不超过1 && 左右子树均平衡
        return ((Math.abs(leftTree-rightTree)<=1)
                && (isBalanced(root.left)) && (isBalanced(root.right)));

    }

② 修改:时间复杂度O(n)

// 但是上面的方法时间复杂度:O(N^2)
    // 改进:边遍历边判断是否平衡,降低时间复杂度为O(n)

    public int maxDepth2(TreeNode root) {
        if (root==null) {
            return 0;
        }
        int leftTree = maxDepth2(root.left);
        int rightTree = maxDepth2(root.right);

        if(leftTree>=0 && rightTree>=0 && (Math.abs(leftTree-rightTree)<=1)) {
            return (Math.max(leftTree,rightTree)+1);
        } else {
            return -1;
        }
    }

    public boolean isBalanced2(TreeNode root) {
        return maxDepth2(root)>=0;
    }

5. 对称二叉树。

对称二叉树

  1. 思路:

左子树==右子树 : 所以还需再写一个两个参数的方法来判断左右子树是否对称
左右子树可能有一个为空、可能都空、可能都不空(值是否相等) &&

  1. 代码:
// 左右子树作为参数 进行是否对称的判断
    public boolean isChildSymmetric(TreeNode leftTree, TreeNode rightTree) {
        // 一个空  都空 都不空(左右子树是否相等)
        if((leftTree==null && rightTree!=null) || (leftTree!=null && rightTree==null)) {
            return false;
        }
        if(leftTree==null && rightTree==null) {
            return true;
        }
        // 都不空
        if(leftTree.val!=rightTree.val) {
            return false;
        }
        // 如果相等,继续其左右子树的判断
        return isChildSymmetric(leftTree.left,rightTree.right) &&
                isChildSymmetric(leftTree.right,rightTree.left);
    }

    public boolean isSymmetric(TreeNode root) {
        // 情况:子树  全空、 一个空 都不空
        if(root==null) {
            return true;
        }
        return isChildSymmetric(root.left,root.right);
    }

6. 二叉树的构建及遍历。

二叉树遍历

  1. 思路:

给定前序遍历,那么就以前序遍历的方式去遍历就可以啦,给一个i去进行遍历,如果非空就创建一个结点,
(先构建根,再去构建左子树和右子树)–这样子递归
定义结点类-可以作为Main的内部类、定义Main类(createTree方法、inOrder方法)注意在输入时,nextLine是否有空格等的输入:ok√
(不用担心越界问题:合法二叉树,递归几次就回退几次)

  1. 代码:
import java.util.Scanner;

// 创建并中序遍历二叉树:
// 该题给定了空树位置,所以是唯一的树!!
// 依据前序遍历结果创建二叉树,然后进行中序遍历并输出
public class Main {
    // 定义节点类为内部类
    static class TreeNode {
        TreeNode left;
        TreeNode right;
        char val;

        public TreeNode(char val) {
            this.val = val;
        }
    }

    // 创建二叉树的方法:createTree
    public int i =0; // 不建议定义静态成员变量:因为可能会有多个用例会有变量i
    public TreeNode createTree(String s) {
        // 如果不是空 就创建结点
        // 前序遍历:先根结点,然后左子树(又按照根-左-右),右子树(根-左-右)
        // 使用i进行遍历
        TreeNode root = null;
        if(s.charAt(i) != '#') {
            // 创建根结点
            root = new TreeNode(s.charAt(i));
            i++;
            // 进行左右子树的创建
            root.left = createTree(s); // 注意此处进行递归是要改变i值的,所以i放到外面
            root.right = createTree(s);
        } else {
            // 跳过
            i++;
        }
        return root;
    }
    // 进行中序遍历:左 根 右 -- 递归
    public void inOrder(TreeNode root) {
        if(root==null) {
            return;
        }
        inOrder(root.left);
        System.out.print(root.val+" ");
        inOrder(root.right);
    }

    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        while(scanner.hasNextLine()) {
            String s = scanner.nextLine();
            Main createTravel = new Main();
            TreeNode root = createTravel.createTree(s);
            createTravel.inOrder(root);
        }
    }
}

7. 二叉树的分层遍历 。

二叉树的层序遍历

  1. 思路:

二叉树的分层遍历:难点就是确定每一层的节点
还是依赖于队列:两次循环:是否为空以及队列大小(每层节点个数)是否>0
其余代码类似于层序遍历

  1. 代码:
public List<List<Integer>> levelOrder(TreeNode root) {
        // 最外层List是层数  第二层List:每层的节点数
        List<List<Integer>> ret = new ArrayList<>(); // 注意治理的声明!
        if(root == null) {
            return ret; // 注意返回的也是ret!!
        }
        Queue<TreeNode> queue = new LinkedList<>(); // 再注意这里!
        queue.offer(root); // 队列存储根节点!

        // 不空就进行循环:队列是否为空 每层大小是否>0
        while(!queue.isEmpty()) { // 判断的是队列 :每一次存储的都是改成的节点
            // 进行每层节点个数获取:
            int size = queue.size();
            // 进行每层存储
            List<Integer> row = new ArrayList<>();
            while(size>0) {
                TreeNode cur = queue.poll(); //弹出栈顶元素
                size--;
                row.add(cur.val);
                if(cur.left!=null) {
                    queue.offer(cur.left);
                }
                if(cur.right!=null) {
                    queue.offer(cur.right);
                }
            }
            // 来到这儿:说明二叉树已经的一层已经遍历结束 即将看队列中的下层
            ret.add(row);
        }
        // 队列为空 说明每一层都已经遍历结束
        return  ret;
    }

8. 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先 。

二叉树的最近公共祖先

  1. 思路:

公共祖先:这两个结点一个在根左边一个在右边 如果其中一个为根,则公共祖先就是根结点
分析:1)假设该树每个结点包含双亲信息,此时最近公共祖先就是两个链表交点;
2)假设是一棵二叉搜索树(二叉排序树:左边结点小于根结点 右边结点大于根结点):其中一个为根、一个大于根一个小于根(根结点就是最近公共祖先)、两个都大于或小于根(递归到相应的左右子树,此时遇到的两个结点中的一个就是最近公共祖先)

  1. 代码:
    ① 二叉搜索树:
// 二叉搜索树:
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null) {
            return null;
        }
        if(root==p || root==q) {
            return root;
        }
        //if((root.val<p.val && root.val>q.val) || (root.val>p.val && root.val<q.val))

        // 其实到这儿之后就有两种情况:都在同一边 or 一左一右
        TreeNode leftNode = lowestCommonAncestor(root.left,p,q); // 先进行左边寻找
        TreeNode rightNode = lowestCommonAncestor(root.right,p,q); // 再进行右边寻找
        // 进行情况的判断:
        if(leftNode!=null && rightNode!=null) {
            // 一左一右
            return root;
        } else if(leftNode!=null) {
            return leftNode;
        } else {
            return rightNode;
        }
    }

② 使用栈进行交点求解:

// 方法2:使用两个栈 (栈:先进后出)
    // stackP:存根结点到p结点的所有结点   stackQ:存根结点到q结点的所有结点
    // 然后进行栈的大小比较,大的先出差个结点 然后再同时出并比较是否相等,首次相等的就是最近公共祖先!
    // 难点:如何找到根结点到指定结点的路径!
    // 补充:如果结点的值存在一样的情况,出栈就比较地址!!!(找不一样的比较)

    // 得到根结点到指定结点的路径并存储在栈中
    private boolean getPath(TreeNode root, TreeNode node, Stack<TreeNode> stack) {
        if(root==null || node==null) {
            return false;
        }
        // 来到这儿说明既不相等又不为空 就进行存储
        // 注意下面顺序不能颠倒:不管相不相等先入栈 入栈后才比较!!
        // (不然如果直接相等了就根本不会入栈!!)
        stack.push(root);
        if(root==node) {
            return true;
        }
        // 然后进行其左右子树的递归
        // 结点放入栈之后还要与指定结点进行比较,看是否相等,是否还有继续的必要
        boolean ret1 = getPath(root.left,node,stack);
        // 进行返回值接收并判断true
        // 不能判断false:因为还可能存在于右边 右边还没有进行比较!!
        if(ret1) {
            return true;
        }
        // 来到这儿说明:左边不等 则右边进行递归及比较
        boolean ret2 = getPath(root.right,node,stack);
        if(ret2) {
            return true;
        }
        // 此时:左右节点都没有找到node,则当前根节点要出栈 并且返回false
        stack.pop();
        return false;
    }
    public TreeNode lowestCommonAncestor2(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null || p==null || q==null) {
            // 只要其中一个结点为空就说明找不到
            return null;
        }
        // 来到这儿:说明 有进行寻找路径比较的意义
        Stack<TreeNode> stackP = new Stack<>();
        getPath(root,p,stackP);
        Stack<TreeNode> stackQ = new Stack<>();
        getPath(root,q,stackQ);
        int sizeP = stackP.size();
        int sizeQ = stackQ.size();
        if(sizeP<sizeQ) {
            int tmp = sizeQ-sizeP;
            // Q进行出栈已达到一样数量
            while(tmp!=0) {
                stackQ.pop();
                tmp--; // 不要忘记循环条件的改变!
            }
        } else {
            int tmp = sizeP-sizeQ;
            // Q进行出栈已达到一样数量
            while(tmp!=0) {
                stackP.pop();
                tmp--; // 不要忘记循环条件的改变!
            }
        }
        // 到这儿说明:此时两个栈中的元素个数一致
        // 循环进行比较:注意循环条件
        // 其实栈中存储的是结点,那么比较大小比较的是结点的地址
        while(!stackP.isEmpty() && !stackQ.isEmpty()) {
            if(stackP.peek() == stackQ.peek()) {
                return stackP.peek();
            } else {
                // 不相等就两个都弹出
                stackP.pop();
                stackQ.pop();
            }
        }
        // 如果来到这儿就说明不满足以上 即找不到
        return null;
    }

9. 二叉搜索树转换成排序双向链表。

二叉搜索树与双向链表

  1. 思路:

双向链表:也就是LinkedList,比较重要的是前驱prev和后继next。
双端队列:可以顺序和逆序访问(即两边都可以进or出;也可以同一边进同一边出–即栈的特性)
1)排序:使用中序遍历这棵二叉树就ok
2)如何变成双向链表?前驱和后继? – 前驱是left 后继是right,但是注意有一点变化,并不是直接跟原来二叉树的左右子树(左右子结点)一样,而是物理上的左右:其实也就是中序遍历的结果!!
时间复杂度是O(n):即一边遍历一边修改指向

  1. 代码:
// 二叉搜索树转换为双向链表:

    // 写一个中序遍历的方法:
    public TreeNode prev = null;
    public void midTravel(TreeNode root) {
        if(root==null) {
            return;
        }
        midTravel(root.left);
        //System.out.print(root.val + " ");
        // 前驱:其实就是之前遍历的节点(所以要记录当前结点以供下一次使用)
        // 每次递归的记录结点都是会变化的 所以变量定义在外面
        
        root.left = prev;
        // 注意这里要判断是否为空 首次为空 而为空时hi发生空指针异常!
        if(prev != null) {
            prev.right = root;
        }
        prev = root; // 就是进行当前结点的更新
        
        midTravel(root.right);
    }
    public TreeNode Convert(TreeNode pRootOfTree) {
        if(pRootOfTree==null) {
            return null;
        }
        // 要拿到双向链表头结点:从二叉树根结点一直沿着之前的中序遍历往左走,
        // 直到遇到某一个结点的左子树为空,则该结点就是所找的头结点
        midTravel(pRootOfTree);
        TreeNode head = pRootOfTree;
        while(head.left != null) {
            head = head.left;
        }
        return head;
    }

10. 根据一棵树的前序遍历与中序遍历构造二叉树。

从前序与中序遍历构造二叉树

  1. 思路:

先序遍历pi 中序遍历:ib im ie 而pi==im ib和ie分别为左右子树
即:在前序遍历中构建根结点; 在中序遍历数组中找到根结点的位置im; 分别构建根结点root的左右子树
重新写一个方法(参数:前序 中序数组 以及前序下标、后序开始和结尾下标)
再写一个找中序遍历中根结点的方法

  1. 代码:
//从前序与中序遍历构造二叉树:
    // 中序遍历:根结点左边是左子树 右边是右子树!
    // 重新写一个方法:
    // 注意参数preIndex(标记前序遍历的下标),但是后续要经历递归,需要改变下标,但是作为参数就是类似于局部变量
    // 所以拿出来作为全局变量 减少参数
    public int preIndex = 0;
    private TreeNode buildTreeChild(int[] preorder, int[] inorder, int inBegin, int inEnd) {
       // 判断左右子树是否还存在
        if(inBegin>inEnd) {
            return null;
        }
        TreeNode root = new TreeNode(preorder[preIndex]); // 前序遍历构建好根结点
         // 找中序遍历中的根结点位置
        int rootIndex = findInorderIndex(inorder,preorder[preIndex],inBegin,inEnd);
        preIndex++; // 前序遍历的下标将向后移
        // root的左右子树的构建:左子树也就是前序遍历的下一个结点
        // 注意参数:中序遍历的左子树的最大范围就是根结点的左边结点!
        root.left = buildTreeChild(preorder,inorder,inBegin,rootIndex-1);
        root.right = buildTreeChild(preorder,inorder,rootIndex+1,inEnd);
        return root; // 返回建好的二叉树的根结点
    }
    // 找中序遍历中的根结点位置
    private int findInorderIndex(int[] inorder, int val, int inBegin, int inEnd) {
        for (int i = inBegin; i <= inEnd; i++) {
            if(val == inorder[i]) {
                return i;
            }
        }
        return -1;
    }
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        TreeNode root = buildTreeChild(preorder,inorder,0,inorder.length-1);
        return root;
    }

11. 根据一棵树的中序遍历与后序遍历构造二叉树。

从中序与后序遍历构造二叉树

  1. 思路:

类似于 “前序、后序遍历构造二叉树”
逆序遍历 后序遍历,最后一个是根结点,然后是右子树
注意修改!(后序遍历是先右子树再左子树,以及范围)

  1. 代码:
// 从中序与后序遍历构造二叉树:
    public int postIndex ; // 注意其大小声明位置!
    private TreeNode buildTreeChild2(int[] inorder, int[] postorder, int inBegin, int inEnd) {
        // 判断左右子树是否还存在
        if(inBegin>inEnd) {
            return null;
        }
        TreeNode root = new TreeNode(postorder[postIndex]); // 后序遍历构建好根结点
        // 找中序遍历中的根结点位置
        int rootIndex = findInorderIndex2(inorder,postorder[postIndex],inBegin,inEnd);
        postIndex--; // 后序遍历的下标将向前移
        // root的左右子树的构建:右子树也就是后序遍历的前一个结点
        // 注意参数:中序遍历的左子树的最大范围就是根结点的左边结点!
        // 注意:后序遍历先右再左:所以注意以下范围修改!
        //(但是对于中序遍历永远是根左左子树 根右右子树)
        root.right = buildTreeChild2(inorder,postorder,rootIndex+1,inEnd);
        root.left = buildTreeChild2(inorder,postorder,inBegin,rootIndex-1);
        return root; // 返回建好的二叉树的根结点
    }
    // 找中序遍历中的根结点位置
    private int findInorderIndex2(int[] inorder, int val, int inBegin, int inEnd) {
        for (int i = inBegin; i <= inEnd; i++) {
            if(val == inorder[i]) {
                return i;
            }
        }
        return -1;
    }
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        postIndex = postorder.length-1; // √
        return (buildTreeChild2(inorder,postorder,0,inorder.length-1));
    }

12. 二叉树创建字符串。

根据二叉树创建字符串

  1. 思路:

前序遍历过程中,只要左子树不为空就加(,一旦该结点左右子树为空就开始加);如果直接左右子树为空就没有括号; 如果左子树为空右子树不空就在左子树位置加()。
即:左右子树是分别加括号的 --使用递归实现

  1. 代码:
// 根据二叉树创建字符串:
    private void tree2strChild(TreeNode root,StringBuilder sb) {
        if(root==null) {
            return ;
        }
        // 来到这儿:非空 进行判断左子树
        sb.append(root.val);
        if(root.left!=null) {
            sb.append('(');
            // 然后进行递归
            tree2strChild(root.left,sb);
            sb.append(')');
        } else {
            // 左子树为空 需要判断右子树是否为空 来决定加不加括号!
            if(root.right==null) {
                return;
            } else {
                // 左空右不空
                sb.append("()"); // 给左
            }
        }
        // 右边单独给一个描述
        if(root.right==null) {
            return;
        } else {
            sb.append('('); // 给右
            tree2strChild(root.right,sb); //递归
            sb.append(')');
        }
    }
    public String tree2str(TreeNode root) {
        StringBuilder sb = new StringBuilder();
        tree2strChild(root,sb);
        // 调用方法之后sb就改变
        return sb.toString(); // 注意这里的返回形式!!
    }

13. 二叉树前序非递归遍历实现 。

二叉树的前序遍历

  1. 思路:

放入栈然后打印,如果是左为空就弹出,如果左右都为空,就弹出队列中的下一个并且找其right。
一定要注意双层循环!

  1. 代码:
public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        // 使用栈存储:
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        // 注意双层循环:否则如果左为空而右不为空时会出现错误!--重难点!!
        while(cur!=null || !stack.isEmpty()) {
            while(cur!=null) {
                // 入栈
                stack.push(cur);
                /* 这里可以没有打印 直接存到链表中
                // 打印
                System.out.print(cur.val + " ");*/
                // 加到链表中
                list.add(cur.val);
                cur = cur.left; // 左子树
            }
            // 来到这儿:左子树为空 弹出元素 看右子树
            TreeNode top = stack.pop();
            cur = top.right;
        }
        return list;
    }

14. 二叉树中序非递归遍历实现。

二叉树的中序遍历

  1. 思路:

类似于前序遍历,但是要注意结点不打印。
只有当左子树为空的时候才进行结点的弹出以及打印;然后cur=top.right

  1. 代码:
public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        if(root==null) {
            return ret;
        }
        // 不为空:循环入栈--注意循环层数
        Stack<TreeNode> stack = new Stack<>();
        // 注意定义一个当前结点
        TreeNode cur = root;
        while(cur!=null || !stack.isEmpty()) {
            while ((cur!=null)) {
                stack.push(cur);
                // 不打印 而是走向左子树
                cur = cur.left;
            }
            // cur为空:弹出并打印(加到链表中)
            TreeNode top = stack.pop();
            ret.add(top.val);
            cur = top.right; // 走向右边
        }
        return ret;
    }

15. 二叉树后序非递归遍历实现。

二叉树的后序遍历

  1. 思路:

后序遍历:左子树–右子树–根结点
注意要判断是否已经打印过!!-- 这是一个重点!!

  1. 代码:
public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ret = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        TreeNode prev = null;
        if(root==null) {
            return ret;
        }
        // 非空--进行循环
        while(cur!=null || !stack.isEmpty()) {
            while(cur!=null) {
                stack.push(cur);
                cur = cur.left;
            }
            // 此时cur==null 获取栈顶元素但不弹出
            TreeNode top = stack.peek();
            // 判断右子树是否存在
            if(top.right==null || top.right==prev) {
                stack.pop();
                System.out.print(top.val + " ");
                ret.add(top.val);
                prev = top; // 一定要声明这个赋值!! 记录之前的节点
            } else {
                cur = top.right;
            }
        }
        return ret;
    }

THINK

  1. 注意二叉树递归是重点
  2. 注意思路!!
  3. 注意作图!!
  4. 注意思维以及条件!
  5. 是重点!!
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

'Dream是普通小孩耶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值