LeetCode_二叉树刷题笔记1(java)

659 阅读5分钟

这个系列主要是记录我刷题的过程。重点是每一类型题解法的循序渐进,按着这个顺序基本每一题都能做出来。而不是某一题的解法,所以适合打算大量刷题的人参考。二叉树相关的刷题顺序是参考 代码随想录,感兴趣的可以到该公众号上下载对应pdf。

LeetCode_二叉树刷题笔记4(java)

LeetCode_二叉树刷题笔记3(Java)

LeetCode_二叉树刷题笔记2(java)

LeetCode_二叉树刷题笔记1(java)

image-20210218224736818

遍历

二叉树的遍历可以分为:深度优先遍历和广度优先遍历。

常说的:前序遍历中序遍历后续遍历就是深度优先的方式。

而深度优先的方式又有:递归迭代两种。

递归: 代码简洁但空间负责度高

迭代: 虽然空间会省一点,但是使用了Stack,所以时间上会差一些

深度优先

深度优先遍历,其实就是一个树枝一个树枝的遍历。

144. 二叉树的前序遍历

难度中等513

给你二叉树的根节点 root ,返回它节点值的 前序 遍历。

示例 1:

img
输入:root = [1,null,2,3]
输出:[1,2,3]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

示例 4:

img
输入:root = [1,2]
输出:[1,2]

示例 5:

img
输入:root = [1,null,2]
输出:[1,2]

提示:

  • 树中节点数目在范围 [0, 100]
  • -100 <= Node.val <= 100

递归写法

/**
 * 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> result=new ArrayList();

        preorder(root,result);

        return result;

    }
    private void preorder(TreeNode root,List<Integer> result){
        if(root==null) return;
        result.add(root.val);
        preorder(root.left,result);
        preorder(root.right,result);
    }
}

image-20210218121203178

看上面的时间效率虽然很好,但是空间复杂度很差

迭代写法

/**
 * 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> result= new ArrayList();
        preorder(root,result);

        return result;

    }
    private void preorder(TreeNode root,List<Integer> result){
        Stack<TreeNode> stack =new Stack();
        while(root!=null||!stack.isEmpty()){
            while(root!=null){
                result.add(root.val);
                stack.push(root);
                root=root.left;
            }

            TreeNode cur=stack.pop();
            root=cur.right;
        }
        
    }
}

145. 二叉树的后序遍历

难度中等521

给定一个二叉树,返回它的 后序 遍历。

示例:

输入: [1,null,2,3]  
   1
    \
     2
    /
   3 

输出: [3,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> postorderTraversal(TreeNode root) {
        List<Integer> result = new ArrayList();
        postorder(root,result);
        return result;
    }

    private void postorder(TreeNode root,List<Integer> result){
        if(root==null)return;
        postorder(root.left,result);
        postorder(root.right,result);
        result.add(root.val);
    }
}

image-20210218121803000

迭代写法

/**
 * 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> result = new ArrayList<Integer>();
        Stack<TreeNode> stack = new Stack<TreeNode>();

        while(root!=null||!stack.isEmpty()){
            while(root!=null){
                result.add(root.val);
                stack.push(root);
                root = root.right;
            }

            TreeNode cur = stack.pop();
            root = cur.left;
        }
        Collections.reverse(result);

        return result;
    }
}

94. 二叉树的中序遍历

难度中等858

给定一个二叉树的根节点 root ,返回它的 中序 遍历。

示例 1:

img
输入:root = [1,null,2,3]
输出:[1,3,2]

示例 2:

输入:root = []
输出:[]

示例 3:

输入:root = [1]
输出:[1]

示例 4:

img
输入:root = [1,2]
输出:[2,1]

示例 5:

img
输入:root = [1,null,2]
输出:[1,2]

提示:

  • 树中节点数目在范围 [0, 100]
  • -100 <= Node.val <= 100

递归

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

    private void inorder(TreeNode root,List<Integer> result){
        if(root==null)return ;
        inorder(root.left,result);
        result.add(root.val);
        inorder(root.right,result);
    }
}

image-20210218131838092

迭代

中序遍历的迭代写法跟前两种有点不同,主要是记录参数的时候。

/**
 * 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> result = new ArrayList();
        Stack<TreeNode> stack = new Stack();

        while(root!=null||!stack.isEmpty()){

            while(root!=null){
                stack.push(root);
                root = root.left;
            }
            root = stack.pop();
            result.add(root.val);
            root = root.right;
        }
        return result;
    }
}

image-20210218185933731

广度优先

在二叉树中广度优先,其实就是一层一层的遍历,即 层序遍历

这篇文章是LeetCode上题解,感觉关于层序遍历说的挺好

广度优先借助了 队列的先进先出的特性。模板如下:

void bfs(TreeNode root) {
    Queue<TreeNode> queue = new ArrayDeque<>();
    queue.add(root);//根节点
    while (!queue.isEmpty()) {
        TreeNode node = queue.poll(); // 取出最先进入的Node
        if (node.left != null) {
            queue.add(node.left);
        }
        if (node.right != null) {
            queue.add(node.right);
        }
    }
}

102. 二叉树的层序遍历

难度中等773

给你一个二叉树,请你返回其按 层序遍历 得到的节点值。 (即逐层地,从左到右访问所有节点)。

示例: 二叉树:[3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回其层序遍历结果:

[
  [3],
  [9,20],
  [15,7]
]
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrder(TreeNode root) {

        List<List<Integer>> result =new ArrayList();
        //双端队列
        Queue<TreeNode> queue = new ArrayDeque();
        if(root!=null){
            queue.add(root);
        }
        while(!queue.isEmpty()){
            //记录每一层的数据
            List<Integer> level = new ArrayList();
            //当前层的宽度
            int N = queue.size();

            for(int i = 0;i < N;i++){
                 //取出最前面的Node
                 TreeNode node =queue.poll();
                 level.add(node.val);

                 if(node.left!=null){
                     queue.add(node.left);
                 }
                 if(node.right!=null){
                     queue.add(node.right);
                 }
            }
            result.add(level);
        }

        return result;
    }
}

image-20210218214155130

107. 二叉树的层序遍历 II

难度简单405

给定一个二叉树,返回其节点值自底向上的层序遍历。 (即按从叶子节点所在层到根节点所在的层,逐层从左向右遍历)

例如: 给定二叉树 [3,9,20,null,null,15,7],

    3
   / \
  9  20
    /  \
   15   7

返回其自底向上的层序遍历为:

[
  [15,7],
  [9,20],
  [3]
]

本题与上一道的区别在于,需要将上一道最后返回的result 倒序即可:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<List<Integer>> levelOrderBottom(TreeNode root) {
        List<List<Integer>> result = new ArrayList();
        Queue<TreeNode> deque = new ArrayDeque();

        if(root!=null){
            deque.add(root);
        }
        while(!deque.isEmpty()){
            List<Integer> level =new ArrayList();
            int n =deque.size();
            for(int i = 0; i < n; i++){
                TreeNode node =deque.poll();
                level.add(node.val);
                if(node.left!=null){
                    deque.add(node.left);
                }
                if(node.right!=null){
                    deque.add(node.right);
                }
            }
            //这样先遍历出来的上层,排序在后面。
            result.add(0,level);
        }

        return result;

    }
}

199. 二叉树的右视图

难度中等405

给定一棵二叉树,想象自己站在它的右侧,按照从顶部到底部的顺序,返回从右侧所能看到的节点值。

示例:

输入: [1,2,3,null,5,null,4]
输出: [1, 3, 4]
解释:

   1            <---
 /   \
2     3         <---
 \     \
  5     4       <---

这一题与之前的区别是:本题只需要记录每一行的最后一个元素即可:

/**
 * 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> rightSideView(TreeNode root) {
        List<Integer> result = new ArrayList();
        Queue<TreeNode> deQueue = new ArrayDeque();

        if(root!=null){
            deQueue.add(root);
        }
        while(!deQueue.isEmpty()){
            int n = deQueue.size();
            for(int i = 0; i < n; i++){
                TreeNode node = deQueue.poll();
                if(i==n-1){
                    //只需要记录这一行最后一个即可
                   result.add(node.val);
                }
                if(node.left!=null){
                    deQueue.add(node.left);
                }
                if(node.right!=null){
                    deQueue.add(node.right);
                }  
            }
        }
        return result;
    }
}

637. 二叉树的层平均值

难度简单236

给定一个非空二叉树, 返回一个由每层节点平均值组成的数组。

示例 1:

输入:
    3
   / \
  9  20
    /  \
   15   7
输出:[3, 14.5, 11]
解释:
第 0 层的平均值是 3 ,  第1层是 14.5 , 第2层是 11 。因此返回 [3, 14.5, 11]

提示:

  • 节点值的范围在32位有符号整数范围内。

本地与上面不同的在于获取每行的总和然后获取平均数

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> result = new ArrayList();
        Queue<TreeNode> deQueue = new ArrayDeque();

        if(root!=null){
            deQueue.add(root);
        }
        while(!deQueue.isEmpty()){
            int n = deQueue.size();
            //注意这里使用Double类型
            Double levelSum =0.0;
            for(int i = 0; i < n; i++){
                TreeNode node = deQueue.poll();
                //加
                levelSum=levelSum+node.val;
                if(node.left!=null){
                    deQueue.add(node.left);
                }
                if(node.right!=null){
                    deQueue.add(node.right);
                }  
            }
            result.add(levelSum/n);
        }
        return result;
    }
}

429. N 叉树的层序遍历

难度中等130

给定一个 N 叉树,返回其节点值的层序遍历。(即从左到右,逐层遍历)。

树的序列化输入是用层序遍历,每组子节点都由 null 值分隔(参见示例)。

示例 1:

img

输入:root = [1,null,3,2,4,null,5,6]
输出:[[1],[3,2,4],[5,6]]

示例 2:

img

输入:root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14]
输出:[[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]]

提示:

  • 树的高度不会超过 1000
  • 树的节点总数在 [0, 10^4] 之间

本题与 102. 二叉树的层序遍历的区别在于当前每个Node下可能有N个Node。

/*
// 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 {
    public List<List<Integer>> levelOrder(Node root) {
        
        List<List<Integer>> result =new ArrayList();
        //双端队列
        Queue<Node> queue = new ArrayDeque();
        if(root!=null){
            queue.add(root);
        }
        while(!queue.isEmpty()){
            //记录每一层的数据
            List<Integer> level = new ArrayList();
            //当前层的宽度
            int N = queue.size();

            for(int i = 0;i < N;i++){
                 //取出最前面的Node
                 Node node =queue.poll();
                 level.add(node.val);
								//N个结点
                for(Node child : node.children){
                    queue.add(child);
                }  
            }
            result.add(level);
        }

        return result;
    }
}

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

难度中等124

您需要在二叉树的每一行中找到最大的值。

示例:

输入: 

          1
         / \
        3   2
       / \   \  
      5   3   9 

输出: [1, 3, 9]

本题与637. 二叉树的层平均值基本一样,一个求最大值,一个求平均值

/**
 * 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> largestValues(TreeNode root) {
         List<Integer> result = new ArrayList();
        Queue<TreeNode> deQueue = new ArrayDeque();

        if(root!=null){
            deQueue.add(root);
        }
        while(!deQueue.isEmpty()){
            int n = deQueue.size();
            //当前层最大值,由于Node可能是负数,所以这里不能是0
            Integer levelMax = Integer.MIN_VALUE;
            for(int i = 0; i < n; i++){
                TreeNode node = deQueue.poll();
                //获取较大的值
                levelMax=Math.max(node.val,levelMax);
                if(node.left!=null){
                    deQueue.add(node.left);
                }
                if(node.right!=null){
                    deQueue.add(node.right);
                }  
            }
            result.add(levelMax);
        }
        return result;
    }
}

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

难度中等395

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:

struct Node {
  int val;
  Node *left;
  Node *right;
  Node *next;
}

填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL

初始状态下,所有 next 指针都被设置为 NULL

进阶:

  • 你只能使用常量级额外空间。

  • 使用递归解题也符合要求,本题中递归程序占用的栈空间不算做额外的空间复杂度。

示例:

img

输入:root = [1,2,3,4,5,6,7]
输出:[1,#,2,3,#,4,5,6,7,#]
解释:给定二叉树如图 A 所示,你的函数应该填充它的每个 next 指针,以指向其下一个右侧节点,如图 B 所示。序列化的输出按层序遍历排列,同一层节点由 next 指针连接,'#' 标志着每一层的结束。

提示:

  • 树中节点的数量少于 4096
  • -1000 <= node.val <= 1000
/*
// 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) {
        //这里需要记录根节点
        Node connectedNode=root;
        Queue<Node> queue = new ArrayDeque();
        if(root!=null){
            queue.add(root);
        }
       
        while(!queue.isEmpty()){
            //当前层的宽度
            int N = queue.size();
            for(int i = 0;i < N;i++){
                 //取出最前面的Node
                  Node node=queue.poll();
                  //非每行的最后一个元素,
                  if(i<N-1){
                      //这里使用peek(),只取值不弹出
                      node.next=queue.peek();
                  }
                 if(node.left!=null){
                     queue.add(node.left);
                 }
                 if(node.right!=null){
                     queue.add(node.right);
                 }
            }
        }

        return connectedNode;
    }
}

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

这道题跟上一题解法没啥区别。虽然说上一题是完全二叉树,但是上一题的解法根本不需要考虑是否为完全二叉树。