代码随想录算法训练营第十三天

递归遍历

二叉树的前,中,后序遍历

题目链接 前序遍历 中序遍历 后序遍历

前序遍历

题解
class Solution {
    public List preorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        preorder(list,root);
        return list;
    }
    public void preorder(List list,TreeNode root){
        if(root == null){
            return ;
        }
        list.add(root.val);
        preorder(list,root.left);
        preorder(list,root.right);
    }
}

中序遍历

题解
class Solution {
    public List inorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        inorder(list, root);
        return list;
    }

    public void inorder(List list, TreeNode root) {
        if (root == null) {
            return;
        }
        inorder(list, root.left);
        list.add(root.val);
        inorder(list, root.right);

    }
}

后序遍历

题解
class Solution {
    public List postorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        postorder(list,root);
        return list;
    }
    public void postorder(List list,TreeNode root){
        if(root == null){
            return ;
        }
        postorder(list,root.left);
        postorder(list,root.right);
        list.add(root.val); 
    }
}

解题思路

使用递归实现树的遍历可以看出前中后序遍历还是挺像的,区别是消费节点的时机不同,例如前序遍历是先消费节点再对左右节点递归处理。

迭代遍历

二叉树的前,中,后序遍历

 题目链接 前序遍历 中序遍历 后序遍历

前序遍历

题解
class Solution {
    public List preorderTraversal(TreeNode root) {
        List list = new ArrayList<>();
        if(root == null){
            return list;
        }
        Stack stack = new Stack<>();
        stack.push(root);
        TreeNode cur = root;
        while(cur != null && !stack.isEmpty()){
            TreeNode node = stack.pop();
            list.add(node.val);
            if(node.right != null){
                stack.push(node.right);
            }
            if(node.left != null){
                stack.push(node.left);
            }
        }
        return list; 
    }
}

中序遍历

题解
class Solution {
    public List inorderTraversal(TreeNode root) {
        List res = new ArrayList<>();
        if(root == null){
            return res;
        }
        Stack stack = new Stack<>();
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()){
            if(cur!=null){
                stack.push(cur);
                cur = cur.left;
            }else {
                cur = stack.pop();
                res.add(cur.val);
                cur = cur.right;
            }
        }
        return res;
    }
}

后序遍历

题解
class Solution {
    public List postorderTraversal(TreeNode root) {
        List res = new ArrayList<>();
        if(root == null){
            return res;
        }
        Stack stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            res.add(node.val);
            if(node.left != null){
                stack.push(node.left);
            }
            if(node.right != null){
                stack.push(node.right);
            }
        }
        Collections.reverse(res);
        return res;
    }
}

解题思路

迭代实现与递归实现非常不同,迭代需要使用while循环遍历整个树,除此之外,还需要容器来保存节点,延迟消费,即访问和消费不在同一时间,这里根据特性使用栈(先进后出)。首先把根节点放入栈中,消费栈中的根节点依次将右节点和左节点放入栈中。这里先将右节点放入栈中,再把左节点放入栈中,目的是消费弹出的时候先弹出左节点再弹出右节点,符合先序的特点(头左右)。

后序与先序的特点一样,这里先讲后序的思路。后序的特点是左右头,但是由于栈先进后出的特性,头进入栈中就一定比左右孩子先消费(头不出来,左右孩子进不了栈中,更不可能被消费)。所以我这里的思路还是先消费头节点,即使用的顺序是头右左,但是最后对数组进行反序。那么这个时候与前序的区别只有两点,先左孩子入栈,再右孩子入栈,最终结果反序。

中序与前序后序不同,头节点如何访问与消费都无法满足中序的特点。中序的思路:遍历的过程中一值往左孩子走,并将左孩子压入栈中,当当前节点为null时说明左节点已经走到头了,从栈中弹出一个节点(最近的左孩子(如果有左孩子的话)),消费该节点,再将该节点的右孩子压入栈中。重复该行为,总结就是有左孩子就往左走并压入栈中,没有左孩子就消费该节点再将其右孩子压入栈中。

层序遍历

题目链接 102.二叉树的层序遍历

题解

class Solution {
    public List> levelOrder(TreeNode root) {
        List> res = new ArrayList>();
        if (root == null) {
            return res;
        }
        Queue que = new LinkedList();
        que.offer(root);
        while (!que.isEmpty()) {
            List itemList = new ArrayList();
            int len = que.size();
            while (len > 0) {
                TreeNode tmpNode = que.poll();
                itemList.add(tmpNode.val);
                if (tmpNode.left != null) {
                    que.offer(tmpNode.left);
                }
                if (tmpNode.right != null) {
                    que.offer(tmpNode.right);
                }
                len --;
            }
            res.add(itemList);

        }
        return res;
    }
}

题目链接 107.二叉树的层次遍历 II

题解

class Solution {
    public List> levelOrderBottom(TreeNode root) {
        List> res = new ArrayList>();
        if (root == null) {
            return res;
        }
        Queue que = new LinkedList();
        que.offer(root);
        while (!que.isEmpty()) {
            List itemList = new ArrayList();
            int len = que.size();
            while (len > 0) {
                TreeNode tmpNode = que.poll();
                itemList.add(tmpNode.val);
                if (tmpNode.left != null) {
                    que.offer(tmpNode.left);
                }
                if (tmpNode.right != null) {
                    que.offer(tmpNode.right);
                }
                len --;
            }
            res.add(itemList);

        }
        List> resList = new ArrayList>();
        for (int i = res.size() - 1; i >= 0; i-- ) {
            resList.add(res.get(i));
        }

        return resList;
    }
}

题目链接 199.二叉树的右视图

题解

class Solution {
    public List rightSideView(TreeNode root) {
        List res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        Queue que = new LinkedList();
        que.offer(root);
        while (!que.isEmpty()) {
            int len = que.size();
            while (len > 0) {
                TreeNode tmpNode = que.poll();
                if(tmpNode.left != null) que.offer(tmpNode.left);
                if(tmpNode.right != null) que.offer(tmpNode.right);
                len --;
                if(len == 0) res.add(tmpNode.val);
            }
        }
        return res;
    }
}

题目链接 637.二叉树的层平均值

题解

class Solution {
    public List averageOfLevels(TreeNode root) {
        List resList = new ArrayList<>();
        if(root == null){
            return resList;
        }
        Queue que = new LinkedList();
        que.offer(root);
        while(!que.isEmpty()){
            int len = que.size();
            int num = len;
            Double sum = 0.0;
            while(len > 0){
                TreeNode tmpNode =  que.poll();
                sum += tmpNode.val;
                if(tmpNode.left != null) que.offer(tmpNode.left);
                if(tmpNode.right != null) que.offer(tmpNode.right);
                len --;
            }
            Double res = sum / num;
            resList.add(res);
        }
        return resList;
    }
}

题目链接 429.N叉树的层序遍历

题解

class Solution {
    public List> levelOrder(Node root) {
        List> resList = new ArrayList>();
        if(root == null){
            return resList;
        }
        Queue que = new LinkedList();
        que.offer(root);
        while(!que.isEmpty()){
            List res = new ArrayList();
            int len = que.size();
            while(len > 0){
                Node tmpNode = que.poll();
                res.add(tmpNode.val);
                List children = tmpNode.children;
                if(children == null){
                    continue;
                }
                for(Node node : children){
                    if(node != null){
                        que.offer(node);
                    }
                }
            len --;
            }
            resList.add(res);
        }
        return resList;
    }
}

题目链接 515.在每个树行中找最大值

题解

class Solution {
    public List largestValues(TreeNode root) {
        List resList = new ArrayList<>();
        Queue que = new LinkedList();
        if(root == null) return resList;
        que.offer(root);
        while(!que.isEmpty()){
            int cur_max = - Integer.MAX_VALUE - 1;
            int len = que.size();
            while(len > 0){
                TreeNode tmpNode = que.poll();
                cur_max = Math.max(cur_max,tmpNode.val);
                if(tmpNode.left != null) que.offer(tmpNode.left);
                if(tmpNode.right != null) que.offer(tmpNode.right);
                len --;
            }
            resList.add(cur_max);
        }
        return resList;
    }
}

题目链接 116.填充每个节点的下一个右侧节点指针

题解

class Solution {
    public Node connect(Node root) {
        if(root == null){
            return root;
        }
        Queue que = new LinkedList();
        que.offer(root);
        while(!que.isEmpty()){
            int len = que.size();
            Node prev = null; // 前一个节点
            
            for(int i = 0; i < len; i++){
                Node curr = que.poll(); // 当前节点
                
                // 处理当前节点的子节点
                if(curr.left != null) que.offer(curr.left);
                if(curr.right != null) que.offer(curr.right);
                
                // 连接当前节点到前一个节点
                if(prev != null){
                    prev.next = curr;
                }
                prev = curr;
            }
            
            // 最后一个节点的next已经默认为null,无需额外设置
        }
        return root;
    }
}

题目链接 117.填充每个节点的下一个右侧节点指针II

题解

class Solution {
    public Node connect(Node root) {
        if(root == null){
            return root;
        }
        Queue que = new LinkedList();
        que.offer(root);
        while(!que.isEmpty()){
            int len = que.size();
            Node prev = null; // 前一个节点
            
            for(int i = 0; i < len; i++){
                Node curr = que.poll(); // 当前节点
                
                // 处理当前节点的子节点
                if(curr.left != null) que.offer(curr.left);
                if(curr.right != null) que.offer(curr.right);
                
                // 连接当前节点到前一个节点
                if(prev != null){
                    prev.next = curr;
                }
                prev = curr;
            }
            
            // 最后一个节点的next已经默认为null,无需额外设置
        }
        return root;
    }
}

题目链接 104.二叉树的最大深度

题解

class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        Queue que = new LinkedList();
        int depth = 0;
        que.offer(root);
        while(!que.isEmpty()){
            int len = que.size();
            depth ++;
            while(len > 0){
                TreeNode tmpNode = que.poll();
                if(tmpNode.left != null) que.offer(tmpNode.left);
                if(tmpNode.right != null) que.offer(tmpNode.right);
                len --;
            }
        }
        return depth;
    }
}

题目链接 111.二叉树的最小深度

题解

class Solution {
    public int minDepth(TreeNode root) {
        if(root == null){
            return 0;
        }
        Queue que = new LinkedList();
        int depth = 0;
        que.offer(root);
        while(!que.isEmpty()){
            int len = que.size();
            depth ++;
            while(len > 0){
                TreeNode tmpNode = que.poll();
                if(tmpNode.left == null && tmpNode.right == null){
                    return depth;
                }
                if(tmpNode.left != null) que.offer(tmpNode.left);
                if(tmpNode.right != null) que.offer(tmpNode.right);
                len --;
            }
        }
        return depth;
    }
}

解题思路

这些题目的思路都一样,流程都一样,只不过对节点的消费方式不同。流程:定义一个队列,消费队列里的元素直到队列为空,在循环内,首先获得队列的大小size(),表示存在的元素个数,也代表上一层的个数。循环元素个数次,每次消费一个元素,将该元素从队列中poll出来并进行消费,如果存在左孩子则将左孩子加入队列中,如果存在右孩子则将右孩子加入队列中,直到队列为空,退出循环,返回要维护的数据。

收获与总结

今天的收获特别大,对树的递归遍历,迭代遍历和相关题目掌握更深了,除此之外,不仅是技术上的提升,一天写了这么多的题目,leetcode上题目完成数的增加也让我更加自信,学习更加有兴趣了。希望天天都有这么多的收获。

你可能感兴趣的:(算法)