《 java 随想录》| LeetCode二叉树高频算法题

前言:这是专门针对java语言讲解的算法解析(题目顺序大致参考《代码随想录》)

思维导图

226. 翻转二叉树


思路

可以发现想要翻转它,其实就把每一个节点的左右孩子交换一下就可以了。

递归法

我们下文以前序遍历为例,通过动画来看一下翻转的过程:

翻转二叉树

递归三部曲:

1. 确定递归函数的参数和返回值

参数就是要传入节点的指针,不需要其他参数了,通常此时定下来主要参数,如果在写递归的逻辑中发现还需要其他参数的时候,随时补充。

TreeNode invertTree(TreeNode root);

2. 确定终止条件

当前节点为空的时候,就返回

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

3. 确定单层递归的逻辑

因为是前序遍历,所以先进行交换左右孩子节点,然后反转左子树,反转右子树。

invertTree(root.left);
invertTree(root.right);
swapChildren(root);

private void swapChildren(TreeNode root) {
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
    }

代码实现:

//DFS递归
class Solution {
   /**
     * 前后序遍历都可以
     * 中序不行,因为先左孩子交换孩子,再根交换孩子(做完后,右孩子已经变成了原来的左孩子),再右孩子交换孩子(此时其实是对原来的左孩子做交换)
     */
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }
        invertTree(root.left);
        invertTree(root.right);
        swapChildren(root);
        return root;
    }

    private void swapChildren(TreeNode root) {
        TreeNode tmp = root.left;
        root.left = root.right;
        root.right = tmp;
    }
}

101. 对称二叉树


递归法

1.确定递归函数的参数和返回值

因为我们要比较的是根节点的两个子树是否是相互翻转的,进而判断这个树是不是对称树,所以要比较的是两个树,参数自然也是左子树节点和右子树节点。

boolean compare(TreeNode left, TreeNode right)

2.确定终止条件

首先要把两个节点为空的情况弄清楚,避免比较数值的时候出现空指针异常。

节点为空的情况有:(注意我们比较的其实不是左孩子和右孩子,所以如下我称之为左节点右节点

  • 左节点为空,右节点不为空,不对称,return false
  • 左不为空,右为空,不对称 return false
  • 左右都为空,对称,返回true

此时已经排除掉了节点为空的情况,那么剩下的就是左右节点不为空:

  • 左右都不为空,比较节点数值,不相同就return false

此时左右节点不为空,且数值也不相同的情况我们也处理了。

        if (left == null && right == null) {
            return true;
        }
        if (left == null && right != null) {
            return false;
        }
        if (left != null && right == null) {
            return false;
        }
        if (left.val != right.val) {
            return false;
        }

注意上面最后一种情况,我没有使用else,而是else if, 因为我们把以上情况都排除之后,剩下的就是 左右节点都不为空,且数值相同的情况。

3.确定单层递归的逻辑

此时才进入单层递归的逻辑,单层递归的逻辑就是处理 左右节点都不为空,且数值相同的情况。

  • 比较二叉树外侧是否对称:传入的是左节点的左孩子,右节点的右孩子。
  • 比较内侧是否对称,传入左节点的右孩子,右节点的左孩子。
  • 如果左右都对称就返回true ,有一侧不对称就返回false 。
        boolean outer = compare(left.left, right.right);
        boolean inner = compare(left.right, right.left);
        boolean res = outer && inner;
        return res;

代码实现:

 /**
     * 递归法
     */
    public boolean isSymmetric1(TreeNode root) {
        return compare(root.left, root.right);
    }

    private boolean compare(TreeNode left, TreeNode right) {

        if (left == null && right != null) {
            return false;
        }
        if (left != null && right == null) {
            return false;
        }

        if (left == null && right == null) {
            return true;
        }
        if (left.val != right.val) {
            return false;
        }
        // 比较外侧
        boolean compareOutside = compare(left.left, right.right);
        // 比较内侧
        boolean compareInside = compare(left.right, right.left);
        return compareOutside && compareInside;
    }

104. 二叉树最大深度


思路

我先用后序遍历(左右中)来计算树的高度。

1.确定递归函数的参数和返回值:参数就是传入树的根节点,返回就返回这棵树的深度,所以返回值为int类型。

int getdepth(TreeNode node)

2.确定终止条件:如果为空节点的话,就返回0,表示高度为0。

if (node == NULL) return 0;

3.确定单层递归的逻辑:先求它的左子树的深度,再求右子树的深度,最后取左右深度最大的数值 再+1 (加1是因为算上当前中间节点)就是目前节点为根节点的树的深度。

int leftdepth = getdepth(node.left);       // 左
int rightdepth = getdepth(node.right);     // 右
int depth = 1 + max(leftdepth, rightdepth); // 中
return depth;

代码实现:

class Solution {
    /**
     * 递归法
     */
    public int maxDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftDepth = maxDepth(root.left);
        int rightDepth = maxDepth(root.right);
        return Math.max(leftDepth, rightDepth) + 1;
    }
}

111. 二叉树最小深度


思路:

这道题直觉上好像和求最大深度差不多,其实还是差不少的。

本题依然是前序遍历和后序遍历都可以,前序求的是深度,后序求的是高度。

  • 二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数或者节点数(取决于深度从0开始还是从1开始)
  • 二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数或者节点数(取决于高度从0开始还是从1开始)

那么使用后序遍历,其实求的是根节点到叶子节点的最小距离,就是求高度的过程,不过这个最小距离 也同样是最小深度。

以下讲解中遍历顺序上依然采用后序遍历(因为要比较递归返回之后的结果,本文我也给出前序遍历的写法)。

本题还有一个误区,在处理节点的过程中,最大深度很容易理解,最小深度就不那么好理解

这就重新审题了,题目说的是:最小深度是从根节点到最近叶子节点的最短路径上的节点数量。注意是叶子节点。什么是叶子节点,左右孩子都为空的节点才是叶子节点!

递归法

1.确定递归函数的参数和返回值:参数为要传入的二叉树根节点,返回的是int类型的深度。

int getDepth(TreeNode* node)

2.确定终止条件:终止条件也是遇到空节点返回0,表示当前节点的高度为0。

if (node == NULL) return 0;

3.确定单层递归的逻辑:这块和求最大深度可就不一样了,一些同学可能会写如下代码:

int leftDepth = getDepth(node->left);
int rightDepth = getDepth(node->right);
int result = 1 + min(leftDepth, rightDepth);
return result;

这个代码就犯了此图中的误区:

如果这么求的话,没有左孩子的分支会算为最短深度。

所以,如果左子树为空,右子树不为空,说明最小深度是 1 + 右子树的深度。

反之,右子树为空,左子树不为空,最小深度是 1 + 左子树的深度。 最后如果左右子树都不为空,返回左右子树深度最小值 + 1 。

代码实现:

class Solution {
    /**
     * 递归法,相比求MaxDepth要复杂点
     * 因为最小深度是从根节点到最近**叶子节点**的最短路径上的节点数量
     */
    public int minDepth(TreeNode root) {
        if (root == null) {
            return 0;
        }
        int leftDepth = minDepth(root.left);
        int rightDepth = minDepth(root.right);
        if (root.left == null) {
            return rightDepth + 1;
        }
        if (root.right == null) {
            return leftDepth + 1;
        }
        // 左右结点都不为null
        return Math.min(leftDepth, rightDepth) + 1;
    }
}

<think>好的,我现在需要回答用户关于Java算法学习资料和教程的请求。首先,用户提到了几个引用,我需要仔细看看这些引用内容,看看有没有直接相关的资源。引用2、3、4、5似乎都和Java算法学习有关,尤其是引用2和4,提到了数据结构与算法Java版教程以及代码随想录的刷题网站,这可能对用户有帮助。 接下来,用户希望得到学习资料和教程的推荐,所以我应该整理这些引用中的相关信息,并给出结构化的建议。根据引用2,动力节点老师的教程适合新手入门,应该放在推荐的首位。引用4提到的代码随想录网站,可能适合刷题和系统学习算法,这也是一个重点。引用3提到的全栈学习路线中包含算法部分,可以作为长期学习的参考。引用5的《糊涂算法》专栏可能适合面试准备,但需要确认是否有Java版本,因为用户明确提到Java相关的资料。 需要注意的是,引用1和引用3中的部分内容可能更偏向于Java全栈或架构师路线,但用户主要关注算法,所以需要筛选出相关的部分。同时,用户可能对如何系统学习、实战练习和面试准备感兴趣,所以应该分这几个方面来组织回答,每个部分推荐相应的资源。 另外,用户提供的引用中有一些是课程或网站,需要确保链接或资源名称正确,但用户给出的引用中没有具体链接,所以可能需要用名称和描述来引导用户搜索。例如,引用2中的“数据结构与算法Java版学习教程”由动力节点老师录制,用户可以在他们的官方网站或教育平台查找。 最后,生成相关问题时要围绕Java算法学习的不同方面,比如基础学习、实战项目、面试准备等,确保问题有针对性,帮助用户进一步探索。</think>### Java算法学习资料与教程推荐 #### 一、系统化学习路径 1. **数据结构与算法基础(Java版)** - **推荐资源**:动力节点老师的《数据结构与算法Java版学习教程》专为Java程序员设计,内容通俗易懂,涵盖基础数据结构(如链表、树、图)和经典算法(排序、查找、动态规划等)[^2]。 - **学习重点**:从复杂度分析入手,逐步掌握线性表、栈、队列的实现与应用。 2. **算法进阶与实战** - **代码随想录刷题网站**:提供LeetCode题目解析(Java实现),按算法类型分类(如双指针、回溯、贪心),适合系统性刷题训练[^4]。 - **示例代码片段**: ```java // 快速排序Java实现 public void quickSort(int[] arr, int low, int high) { if (low < high) { int pivot = partition(arr, low, high); quickSort(arr, low, pivot - 1); quickSort(arr, pivot + 1, high); } } ``` #### 二、专项学习资源 1. **面试算法强化** - **《糊涂算法》专栏**:针对Java面试高频考点(如字符串处理、二叉树遍历),提供解题模板与优化技巧[^5]。 - **高频题目**:Top K问题、链表反转、二分查找变体等。 2. **全栈学习中的算法模块** - **Java全栈学习路线**:引用3的路线图中包含分布式系统中的算法(如一致性哈希、负载均衡策略),适合拓展工程视角[^3]。 #### 三、学习工具与社区 1. **ACM格式输入训练** - 引用4提到的输入处理技巧,可结合牛客网/力扣平台练习Java的输入输出优化。 2. **开源项目实践** - 参考引用3的分布式系统案例,尝试用Java实现简单Raft算法或布隆过滤器。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值