代码随想录算法训练营第11天|递归遍历、迭代遍历、统一迭代、层序遍历

1.递归遍历

文章链接:https://www.programmercarl.com/%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E9%80%92%E5%BD%92%E9%81%8D%E5%8E%86.html#%E7%AE%97%E6%B3%95%E5%85%AC%E5%BC%80%E8%AF%BE
视频链接:https://www.bilibili.com/video/BV1Wh411S7xt

思路:
1.确定递归函数的参数和返回值:
2.确定终止条件
3.确定单层递归的逻辑
针对前中后遍历,递归逻辑基本一致,只是递归的顺序不一致。

①前序遍历

题目链接:https://leetcode.cn/problems/binary-tree-preorder-traversal/description/
在这里插入图片描述

/**
 * 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> list = new ArrayList<>();
        before(root,list);
        return list;
    }

    public void before(TreeNode cur,List<Integer> list){
        if (cur == null) {
            return;
        }

 		//前序遍历顺序:中左右
        list.add(cur.val);
        before(cur.left,list);
        before(cur.right,list);
    }
}

②后序遍历

题目链接:https://leetcode.cn/problems/binary-tree-postorder-traversal/description/
在这里插入图片描述

/**
 * 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> list = new ArrayList<>();
        after(root,list);
        return list;
    }

    public void after(TreeNode cur,List<Integer> list){
        if (cur == null) {
            return;
        }
		//后序遍历顺序:左右中
        after(cur.left,list);
        after(cur.right,list);
        list.add(cur.val);
    }
}

③中序遍历

题目链接:https://leetcode.cn/problems/binary-tree-inorder-traversal/description/
在这里插入图片描述

/**
 * 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> list = new ArrayList<>();
        between(root,list);
        return list;
    }

    public void between(TreeNode cur,List<Integer> list){
        if (cur == null) {
            return;
        }
		//中序遍历顺序:左中右
        between(cur.left,list);
        list.add(cur.val);
        between(cur.right,list);
    }
}

2.迭代遍历

思路:使用栈来实现前序遍历、中序遍历和后序遍历。
每次循环分析都以当前节点(中节点)分析,思考是否将当前值加入结果集,以及对左右节点如何加入栈中。

1.前序遍历
前序遍历是中左右。因此先将当前节点(中节点)的值添加到结果集中,然后为了下次循环先左节点出栈,需要在当前循环中右节点先入栈,左节点后入栈。

解法:
/**
 * 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) {
        Deque<TreeNode> stack = new ArrayDeque<>();
        List<Integer> result = new ArrayList<>();

        if (root == null){
            return result;
        }

        stack.push(root);

        while (!stack.isEmpty()){
            TreeNode cur = stack.pop();
            result.add(cur.val);

            if(cur.right!=null){
                stack.push(cur.right);
            }

            if(cur.left!=null){
                stack.push(cur.left);
            }
        }

        return result;

    }
}

2.中序遍历
中序遍历是左中右。因此当前节点(既是中节点,也是上一节点的左节点)的值不能先放入结果集中,但当前节点要先入栈,接着尝试获取当前节点的左节点,若有,则左节点入栈,并继续获取该节点的左节点;若没有,则出栈,将出栈节点的值放入结果集中,并尝试获取出栈节点的右节点,若有,则右节点入栈;若无,则继续出栈。

解法:
/**
 * 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) {
        Deque<TreeNode> stack = new ArrayDeque<>();
        List<Integer> res = new ArrayList<>();
        if (root == null) {
            return res;
        }
        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;
    }
}

3.后序遍历
后序遍历是左右中。其与前序遍历类似,只是左节点先入栈,右节点后入栈,这样最终列表中的输出结果是中右左,结果翻转后就变成了左右中。

解法:
/**
 * 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<>();
        Deque<TreeNode> stack = new ArrayDeque<>();

        if (root == null){
            return res;
        }

        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;
    }
}

3.统一迭代

文章链接:https://programmercarl.com/二叉树的统一迭代法.html#其他语言版本

4.层序遍历

文章链接:https://programmercarl.com/0102.二叉树的层序遍历.html

1.LeetCode 102.二叉树的层序遍历

题目链接:https://leetcode.cn/problems/binary-tree-level-order-traversal/description/
视频链接:https://www.bilibili.com/video/BV1GY4y1u7b2

在这里插入图片描述

思路:
1️⃣无论递归还是迭代,都是一层一层分析
2️⃣使用队列实现二叉树广度优先遍历
注意:可以使用如下的代码作为层序遍历的模板,解决一类问题。

/**
 * 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 {
    List<List<Integer>> res = new ArrayList<>();

    // /**
    // 递归方式
    //  */
    // public List<List<Integer>> levelOrder(TreeNode root) {
    //     find(root,0);
    //     return res;
    // }

    // public void find(TreeNode cur,Integer deep){
    //     if (cur == null) {
    //         return;
    //     }
    //     deep++;//当前层数

    //     // 判断是否有当前层的列表
    //     if (res.size() < deep) {// 小于deep说明没有当前层的列表
    //         List<Integer> curList = new ArrayList<>();
    //         res.add(curList);
    //     }

    //     res.get(deep-1).add(cur.val);

    //     find(cur.left,deep);
    //     find(cur.right,deep);
    // }

    /**
    迭代方式
     */
    public List<List<Integer>> levelOrder(TreeNode root) {
        iteration(root);
        return res;
    } 

    public void iteration(TreeNode root){
        if(root == null) return;

        Deque<TreeNode> deque = new ArrayDeque<>();
        deque.offer(root);

        while(!deque.isEmpty()){
            // 对当前层进行分析
            Integer len = deque.size();// 获取当前层的节点数
            List<Integer> curList = new ArrayList<>();
            while (len>0) {
                TreeNode cur = deque.poll();
                curList.add(cur.val);
                if (cur.left != null) deque.offer(cur.left);
                if (cur.right != null) deque.offer(cur.right);
                len--;
            }
            res.add(curList);
        }
    }
}

2.LeetCode 107.二叉树的层次遍历 II

题目链接:
https://leetcode.cn/problems/binary-tree-level-order-traversal-ii/description/

在这里插入图片描述

思路:
在模板的基础上翻转最终的列表

解法:
/**
 * 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 {
    List<List<Integer>> res = new ArrayList<>();

    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        //find(root,0);
        find2(root);
        Collections.reverse(res);
        return res;
    }
    /**
    递归方法
     */
    public void find(TreeNode cur,Integer deep){
        if (cur == null) {
            return;
        }

        deep++; // 当前层数

        if (res.size() < deep) {
            List<Integer> curList = new ArrayList<>();
            res.add(curList);
        }

        res.get(deep-1).add(cur.val);

        find(cur.left,deep);
        find(cur.right,deep);
    }

    /**
    迭代法
     */
    public void find2(TreeNode root) {
        if (root == null) {
            return;
        }

        Deque<TreeNode> deque = new ArrayDeque<>();
        deque.offer(root);
        
        while (!deque.isEmpty()) {
            // 对当前层进行分析
            Integer len = deque.size();//当前层的节点数
            List<Integer> curList = new ArrayList<>();

            while (len>0) {
                TreeNode cur = deque.poll();
                curList.add(cur.val);
                if (cur.left != null) deque.offer(cur.left);
                if (cur.right != null)  deque.offer(cur.right);
                len--;
            }
            res.add(curList);
        }

    }
}

3.LeetCode 199.二叉树的右视图

题目链接:https://leetcode.cn/problems/binary-tree-right-side-view/

思路
找到每一层的最后一个数值

在这里插入图片描述

解法:
/**
 * 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 {
    List<List<Integer>> lists = new ArrayList<>();
    public List<Integer> rightSideView(TreeNode root) {
        find(root,0);
        List<Integer> res = new ArrayList<>();
        for (int i = 0;i<lists.size();i++) {
            Integer len = lists.get(i).size();
            res.add(lists.get(i).get(len-1));
        }

        return res;
    }

    /**
    递归方式
     */
    public void find(TreeNode cur,Integer deep){
        if (cur == null) {
            return;
        }
        deep++;
        if (lists.size() < deep) {
            List<Integer> list = new ArrayList<>();
            lists.add(list);
        }
        lists.get(deep-1).add(cur.val);
        find(cur.left,deep);
        find(cur.right,deep);
    }
}

4.LeetCode 637.二叉树的层平均值

题目链接:
https://leetcode.cn/problems/average-of-levels-in-binary-tree/description/

在这里插入图片描述

思路:
对每一层求和,然后求平均值。

解法:
/**
 * 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 {
    List<Double> res = new ArrayList<>();
    public List<Double> averageOfLevels(TreeNode root) {
        find(root);
        return res;
    }
    public void find(TreeNode root) {
        if (root == null) {
            return;
        }

        Deque<TreeNode> deque = new ArrayDeque<>();
        deque.offer(root);
        while(!deque.isEmpty()) {
            // 对每一层进行分析
            Integer len = deque.size();// 当前层的节点数量
            Integer size = len;
            double sum = 0.0;

            while (len>0) {
                TreeNode cur = deque.poll();
                sum += cur.val;
                if (cur.left!=null) {
                    deque.offer(cur.left);
                }

                if (cur.right!=null) {
                    deque.offer(cur.right);
                }
                len--;
            }

            res.add(sum/size);
        }

    }
}

代码分析:
要注意如何转换成double类型。
double sum = 0.0;

5.LeetCode 429. N 叉树的层序遍历

题目链接:https://leetcode.cn/problems/n-ary-tree-level-order-traversal/description/

在这里插入图片描述

思路:
将每层的值放到列表中,和二叉树的层序遍历一致。
注意:使用递归方法效率会更高,用时更少。

解法:
/*
// Definition for a Node.
class Node {
    public int val;
    public List<Node> children;

    public Node() {}

    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, List<Node> _children) {
        val = _val;
        children = _children;
    }
};
*/

class Solution {
    List<List<Integer>> lists = new ArrayList<>();

    public List<List<Integer>> levelOrder(Node root) {
       //find(root,0);
       find2(root);
       return lists;

    }

    /**
    递归
     */
    public void find(Node cur,Integer deep) {
        if (cur == null) {
            return;
        }
        deep++;
        if(lists.size() < deep){
            List<Integer> list = new ArrayList<>();
            lists.add(list);
        }

        lists.get(deep-1).add(cur.val);// 获取当前层的列表,并添加值

        for (Node node:cur.children) {
            find(node,deep);
        }
    }

    /**
    迭代法
     */
    public void find2(Node root) {
        Deque<Node> deque = new ArrayDeque<>();

        if (root==null) {
            return;
        }

        deque.offer(root);
        while (!deque.isEmpty()) {
            // 对每一层进行分析
            Integer len = deque.size();
            List<Integer> list = new ArrayList<>();

            while (len>0) {
                Node cur = deque.poll();
                list.add(cur.val);

                if(cur.children!=null) {
                    deque.addAll(cur.children);
                }
                len--;
            }

            lists.add(list);
        }
    } 
}

6. LeetCode 515.在每个树行中找最大值

题目链接:https://leetcode.cn/problems/find-largest-value-in-each-tree-row/description/

在这里插入图片描述

思路:
找到每一层的最大值
注意:
Integer.MIN_VALUE 最小值
Integer.MAX_VALUE 最大值

解法:
/**
 * 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 {
    List<Integer> res = new ArrayList<>();
    Map<Integer,Integer> map = new TreeMap<>(); 

    public List<Integer> largestValues(TreeNode root) {
        //find(root);
        find2(root,0);
        for (Integer value : map.values()) {
            res.add(value);
        }
        return res;
    }

    /**
    迭代法
     */
    public void find(TreeNode root) {
        if (root == null) {
            return;
        }
        Deque<TreeNode> deque = new ArrayDeque<>();
        deque.offer(root);
        while (!deque.isEmpty()) {
            Integer len = deque.size();// 获取当前层的节点个数
            int max = Integer.MIN_VALUE;// 最大值
            
            while (len>0) {
                TreeNode cur = deque.poll();
                if (cur.val > max) {
                    max = cur.val;
                }

                if (cur.left != null) deque.offer(cur.left);
                if (cur.right != null) deque.offer(cur.right);
                len--;
            }
            res.add(max);// 添加当前层的最大值

        }
    }
    /**
    递归方法
     */
    public void find2(TreeNode cur,Integer deep) {
        if (cur == null) {
            return;
        }
        deep++;
        if (map.get(deep) == null) {
            map.put(deep,Integer.MIN_VALUE);
        }
        if (map.get(deep) < cur.val) {
            map.put(deep,cur.val);
        }

        find2(cur.left,deep);
        find2(cur.right,deep);

    } 
}

7. LeetCode 116.填充每个节点的下一个右侧节点指针

题目链接:https://leetcode.cn/problems/find-largest-value-in-each-tree-row/

在这里插入图片描述

思路:
本题依然是层序遍历,只不过在单层遍历的时候记录一下本层的头部节点,然后在遍历的时候让前一个节点指向本节点就可以了。

解法:
/*
// Definition for a Node.
class Node {
    public int val;
    public Node left;
    public Node right;
    public Node next;

    public Node() {}
    
    public Node(int _val) {
        val = _val;
    }

    public Node(int _val, Node _left, Node _right, Node _next) {
        val = _val;
        left = _left;
        right = _right;
        next = _next;
    }
};
*/

class Solution {
    // public Node connect(Node root) {
    //     find(root);
    //     return root;
    // }

    // public void find(Node root) {
    //     if(root == null) {
    //         return;
    //     }
    //     Deque<Node> deque = new ArrayDeque<>();
    //     deque.offer(root);

    //     while(!deque.isEmpty()){
    //         // 对每一层分别进行分析
    //         Integer len = deque.size();// 获取当前层的节点数量
    //         Node temp = null;
    //         while (len>0) {
    //             Node cur = deque.poll();
    //             if (temp != null) {
    //                 temp.next = cur;
    //             }
    //             temp = cur;
    //             if (cur.left!=null) deque.offer(cur.left);
    //             if (cur.right!=null) deque.offer(cur.right);
    //             len--;
    //         }
    //     }
    // }

    public Node connect(Node root) {
        if (root == null) return null;

        Deque<Node> deque = new LinkedList<>();
        deque.offer(root);

        while(!deque.isEmpty()) {
            Integer len = deque.size();//获取当前层的节点总数

            // 获取当前层的头节点
            Node cur = deque.poll();
            if (cur.left != null) deque.offer(cur.left);
            if (cur.right != null) deque.offer(cur.right);

            for (int i=1;i<len;i++) {
                Node next = deque.poll();// 获取下一个节点
                if (next.left != null) deque.offer(next.left);
                if (next.right != null) deque.offer(next.right);
                cur.next = next;
                cur = next;
            }
        }

        return root;
    }
}

8. LeetCode 117.填充每个节点的下一个右侧节点指针II

题目链接:https://leetcode.cn/problems/populating-next-right-pointers-in-each-node-ii/description/
在这里插入图片描述
思路:
与上一题解法无差别

9.LeetCode 104.二叉树的最大深度

题目链接:https://leetcode.cn/problems/maximum-depth-of-binary-tree/description/

在这里插入图片描述

思路:
层序遍历的最终层数。
注意:方法的参数不要设置局部变量deep,其在方法内部的修改,不会影响全局变量deep。
在Java中,基本数据类型(如int)的参数传递是按值传递的,这意味着当你传递一个基本数据类型的变量给方法时,实际上传递的是该变量的一个副本。因此,在方法内部对该变量的任何修改都不会影响到原始变量。
若find方法接受一个TreeNode和一个int类型的deep作为参数,则每次递归调用find方法时,传递的是deep的一个副本,而不是Solution类中声明的deep字段。因此,在find方法内部对deep的任何增加都不会影响到Solution类中的deep字段。
这就是为什么在递归完成后,maxDepth方法返回的deep值仍然是0
,因为Solution类中的deep字段从未被修改过。
为了避免这个问题,你可以将deep作为一个引用类型(如Integer)传递,这样传递的就是引用的副本,而引用指向的是同一个对象,因此在方法内部对对象的修改会反映到原始对象上。但是,在这种情况下,通常更好的做法是使用全局变量,并在递归过程中直接更新它,正如之前修改后的代码示例所示。

解法:
/**
 * 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 {
    int deep = 0;
    public int maxDepth(TreeNode root) {
        find(root,deep);
        return deep;
    }

    public void find(TreeNode cur,int curDeep){
        if (cur == null) {
            return;
        }
        curDeep++;
        if (curDeep>deep) deep = curDeep;
        find(cur.left,curDeep);
        find(cur.right,curDeep);
    }
}

10.LeetCode 111.二叉树的最小深度

题目链接:https://leetcode.cn/problems/minimum-depth-of-binary-tree/description/

在这里插入图片描述

思路:
注意:只有当左右孩子都为空的时候,才说明遍历的最低点了。如果其中一个孩子为空则不是最低点。

解法:
/**
 * 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 {
    int deep = 0;
    public int minDepth(TreeNode root) {
        find(root,0);
        return deep;
    }

    public void find(TreeNode cur,int curDeep){
        if (cur==null) {
            return;
        }
        curDeep++;
        if (cur.left == null && cur.right == null) {
            if (deep == 0 || curDeep < deep) {
                deep = curDeep; // 更新全局变量deep
            }
        }

        find(cur.left,curDeep);
        find(cur.right,curDeep);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值