二叉树4种遍历 迭代 栈/队列 多种写法

前序、中序、后序:栈+迭代,时间O(N),空间O(H),H为二叉树的层高
层序:队列+迭代,时间O(N),空间O(X),X为某层结点数(结点最多的那一层)
先推荐更强的Morris算法,空间仅需O(1):Morris二叉树遍历算法
二叉树4种遍历 迭代 栈/队列 多种写法

前序

leetcode144. 二叉树的前序遍历

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        LinkedList<TreeNode> stack = new LinkedList<>();  //链栈
        stack.push(root);     //根结点压栈
        while(!stack.isEmpty()){   //栈内还有元素
            TreeNode node = stack.pop();  //弹栈
            if(node == null) continue;    //栈顶为空结点
            list.add(node.val);           //输出
            stack.push(node.right);       //右孩子压栈
            stack.push(node.left);        //左孩子压栈,栈特性:后进先出
        }
        return list;
    }
}

中序:leetcode94. 二叉树的中序遍历

中序写法1

循环条件理解稍复杂

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        LinkedList<TreeNode> stack = new LinkedList<>();  //链栈
        while(!stack.isEmpty() || root!=null){  //栈内还有元素或root不为空
            if(root!=null){ //root不为空,继续处理左子树
                stack.push(root);  //压栈
                root = root.left;  //继续处理左子树
            }else{
                //root为空,左子树处理完毕
                TreeNode node = stack.pop(); //弹栈
                list.add(node.val); //输出
                root = node.right;  //去右子树
            }
        }
        return list;
    }
}

中序写法2

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> list = new ArrayList<>();
        LinkedList<TreeNode> stack = new LinkedList<>();  //链栈
        TreeNode node = root; //node初始指向根结点root
        while(node!=null){    //将树的最左端结点全部压栈
            stack.push(node); //压栈
            node = node.left; //继续左孩子
        }
        while(!stack.isEmpty()){  //栈内还有结点
            node = stack.pop();   //弹栈
            list.add(node.val);   //输出该结点
            node = node.right;    //处理右子树
            while(node!=null){    //将右子树的最左端全部压栈
                stack.push(node);
                node = node.left;
            }
        }
        return list;
    }
}

后序

leetcode145. 二叉树的后序遍历

后序写法1,真后序

使用了HashSet进行标记,结点的左右子树访问完毕才输出,符合后序访问顺序
注意结点输出后要移除标记,否则最终Set会存放所有结点,使空间复杂度到O(N)

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        LinkedList<TreeNode> stack = new LinkedList<>();  //链栈
        Set<TreeNode> visited = new HashSet<>(); //标记结点的左子树是否已访问
        TreeNode cur = root;
        while(!stack.isEmpty() || cur!=null){
            if(cur==null && visited.contains(stack.peek())){
                //当前结点为空,且父结点已访问,说明cur为其父结点的右孩子
                visited.remove(stack.peek()); //移除该标记
                list.add(stack.pop().val);    //输出
            }else if(cur==null){
                //当前结点为空,但父节点未被访问,说明cur为其父结点的左孩子
                visited.add(stack.peek()); //将cur的父结点标记为已访问
                cur = stack.peek().right;  //去右子树
            }else{
                //当前结点不为空,可以继续往左下走
                stack.push(cur);  //压栈
                cur = cur.left;   //继续处理左子树
            }
        }
        return list;
    }
}

后序写法2,假后序

为什么是假后序?
因为是使用头插法,输出结果正确,但实际遍历顺序是前序

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> list = new LinkedList<>();
        LinkedList<TreeNode> stack = new LinkedList<>();  //链栈
        stack.push(root);  //根结点压栈
        while(!stack.isEmpty()){
            TreeNode node = stack.pop(); //弹栈
            if(node==null) continue;     //该结点为空
            stack.push(node.left);     //左孩子先压栈
            stack.push(node.right);    //右孩子后压栈
            list.add(0 , node.val);      //输出,头插
        }
        return list;
    }
}

层序:队列

leetcode102. 二叉树的层序遍历

class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {
        if(root==null) return new LinkedList<>();
        List<List<Integer>> res = new LinkedList<>();    //整棵树list
        LinkedList<TreeNode> queue = new LinkedList<>(); //队列
        queue.offer(root); //根结点入队
        while(!queue.isEmpty()){ //队列不为空
            List<Integer> levelList = new ArrayList<>(); //该层的list
            int count = queue.size();  //该层有count个结点
            for(int i=0; i<count; i++){  //i:0 -> count
                TreeNode node = queue.poll(); //出队
                levelList.add(node.val); //输出该结点
                if(node.left!=null) queue.offer(node.left);   //左孩子入队
                if(node.right!=null) queue.offer(node.right); //右孩子入队
            }
            res.add(levelList); //将该层的list加入整棵树的list
        }
        return res;
    }
}
上一篇:图解Java Stack栈


下一篇:13、shell printf命令:格式化输出语句