Leetcode 刷题记录 10 —— 二叉树

本系列为笔者的 Leetcode 刷题记录,顺序为 Hot 100 题官方顺序,根据标签命名,记录笔者总结的做题思路,附部分代码解释和疑问解答,01~07为C++语言,08及以后为Java语言。

01 二叉树的中序遍历

java 复制代码
/**
 * 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) {
        
    }
}

方法一:递归

java 复制代码
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        inoder(root, res);

        return res;
    }

    public void inoder(TreeNode root, List<Integer> res){
        if(root == null){
            return;
        }

        //中序遍历
        inoder(root.left, res);
        res.add(root.val);
        inoder(root.right, res);
    }
}

如果一个方法的返回值是int,那么调用这个方法的时候可以不写一个整数来接收int值,而是直接调用该方法吗?

是的!你完全可以直接调用一个返回值为 int 的方法,而不接收它返回的值**,编译和运行都不会报错,但具体是否这么写,取决于你有没有用到这个**返回值。

方法二:栈

java 复制代码
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        Deque<TreeNode> stk = new LinkedList<>();

        while(root != null || !stk.isEmpty()){
            while(root != null){
                stk.push(root); //压栈
                root = root.left;
            }
            root = stk.pop(); //弹栈
            res.add(root.val);
            root = root.right;
        }

        return res;
    }
}

方法三:Morris 中序遍历

java 复制代码
class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<>();
        TreeNode pre = null;

        while(root != null){
            if(root.left != null){
                //1.寻找pre
                pre = root.left;
                while(pre.right != null && pre.right != root){
                    pre = pre.right;
                }

                //2.判断pre.right
                if(pre.right == null){
                    pre.right = root;
                    root = root.left; //建链,左移
                }else{
                    res.add(root.val);
                    pre.right = null;
                    root = root.right; //断链,右移
                }
                
            }else{
                res.add(root.val);
                root = root.right;
            }
        }

        return res;
    }
}

为什么要进行pre.right = null;的断链处理?

pre.right = null;的断链处理是为了恢复树的原始结构。这个算法使用了Morris中序遍历,它利用树中的空闲右指针来建立链接,从而避免使用额外的栈空间。

02 二叉树的最大深度

java 复制代码
/**
 * 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 int maxDepth(TreeNode root) {
        
    }
}

方法一:递归

java 复制代码
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }

        int leftHeight = maxDepth(root.left);
        int rightHeight = maxDepth(root.right);
        return Math.max(leftHeight, rightHeight) + 1;
    }
}

Math.max啥意思?

Math.max 是 Java 中 Math 类的一个静态方法,它用于返回两个给定数中的最大值。

方法二:广度优先搜索

java 复制代码
class Solution {
    public int maxDepth(TreeNode root) {
        if(root == null){
            return 0;
        }

        //广度优先遍历 queue
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        int answer = 0;

        while(!queue.isEmpty()){
            //a.获取该行长度
            int size = queue.size();
            while(size > 0){
                //b.遍历该行所有结点,添加所有孩子结点
                TreeNode node = queue.poll();
                if(node.left != null){
                    queue.offer(node.left);
                }
                if(node.right != null){
                    queue.offer(node.right);
                }
                size--;
            }
            //c.每遍历一行,answer加一
            answer++;
        }

        return answer;
    }
}

03 翻转二叉树

java 复制代码
/**
 * 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 TreeNode invertTree(TreeNode root) {
        
    }
}

方法一:递归

java 复制代码
class Solution {
    public TreeNode invertTree(TreeNode root) {
        //0.特殊情况判断
        if(root == null){
            return root;
        }

        //1.获取左右孩子指针
        TreeNode left = invertTree(root.left);
        TreeNode right = invertTree(root.right);

        //2.交换左右孩子指针
        root.left = right;
        root.right = left;
        
        return root;
    }
}

04 对称二叉树

java 复制代码
/**
 * 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 boolean isSymmetric(TreeNode root) {
        
    }
}

方法一:递归

java 复制代码
class Solution {
    public boolean isSymmetric(TreeNode root) {
        if (root == null) {
        	return true;
    	}
        return check(root.left, root.right);
    }

    public boolean check(TreeNode p, TreeNode q){
        if(p == null && q == null){
            return true;
        }
        if(p == null || q == null){
            return false;
        }

        return p.val == q.val && check(p.left, q.right) && check(p.right, q.left);
    }
}

① 如果TreeNode rootnull咋办,根本就没有root.leftroot.right吧?

如果 rootnull,那么传递给 check(root.left, root.right) 的参数就是 check(null, null)

② 为什么if(p == null && q == null)之后直接return true;,万一还有别的结点没有检查呢,直接就返回正确吗?

当递归到底叶子节点时,不存在还未检查的节点,因为递归调用本身会遍历所有对应的节点对。

方法二:队列

java 复制代码
class Solution {
    public boolean isSymmetric(TreeNode root) {
        return check(root, root);
    }

    public boolean check(TreeNode u, TreeNode v){
        //1.创建并加入队列,头结点
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(u);
        queue.offer(v);

        while(!queue.isEmpty()){
            //2.移出队列,判断结点状况
            u = queue.poll();
            v = queue.poll();
            if(u == null && v == null){
                continue;
            }
            if((u == null || v == null) || u.val != v.val){
                return false;
            }

            //3.加入队列,左右孩子结点
            queue.offer(u.left);
            queue.offer(v.right);

            queue.offer(u.right);
            queue.offer(v.left);
        }

        return true;
    }
}

为什么u == null && v == null成立要进行continue,而不是直接返回true

如果 u == null && v == null,这意味着当前的两个节点都是空的,但这只是树的一部分,整体是否对称还需要继续检查其他节点,只有在所有节点都已经处理并且没有发现不对称时,才会返回 true

05 二叉树的直径

java 复制代码
/**
 * 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 int diameterOfBinaryTree(TreeNode root) {
        
    }
}

方法一:递归

java 复制代码
class Solution {
    int ans = 0;
    public int diameterOfBinaryTree(TreeNode root) {
        depth(root);
        return ans-1;
    }

    public int depth(TreeNode root){
        if(root == null){
            return 0;
        }

        int L = depth(root.left);
        int R = depth(root.right);
        ans = Math.max(ans, L + R + 1);
        return Math.max(L, R) + 1;
    }
}
相关推荐
这是个栗子20 分钟前
前端开发中的常用工具函数(八)
开发语言·前端·javascript
凤凰院凶涛QAQ33 分钟前
《Java版数据结构 & 集合类剖析》栈与队列:“push/pop 是栈的灵魂,offer/poll 是队列的骨架——四组 API,两种人生”
java·开发语言·数据结构
研☆香41 分钟前
为什么变量声明在赋值前也能使用?—— 深入理解 JavaScript 变量提升与作用域
开发语言·前端·javascript
右耳朵猫AI1 小时前
PHP周刊2026W28 | 安全更新、Laravel 13.18、Twig 3.28
开发语言·php·laravel
科技大视界1 小时前
投资AI项目,传统尽调不够用了——李章虎律师拆解算法、数据、算力三大雷区
人工智能·算法·数据挖掘
second601 小时前
第一部分:快速上手 —— 建立 C++ 基本语法与编程范式
开发语言·c++
郝学胜-神的一滴1 小时前
算法实战:最小k个数——大顶堆的优雅解法
开发语言·数据结构·c++·python·程序人生·算法·排序算法
Irissgwe1 小时前
算法滑动窗口
数据结构·算法
bksczm1 小时前
linux之线程概念和控制
linux·开发语言·c++
掘金_答案1 小时前
上线那天,一个 ConcurrentHashMap 差点送走我的 AI 客服——3 天排查 JVM 血泪史
java·后端·架构