[java]——leetcode刷题(深度优先搜索)(简单21+中等25)

文章目录

前言

写的太多就会卡卡的。。
实在太卡了。。

简单题

1.面试题 04.02 最小高度数

题目描述:

给定一个有序整数数组,元素各不相同且按升序排列,编写一个算法,创建一棵高度最小的二叉搜索树。
示例:
给定有序数组: [-10,-3,0,5,9],
一个可能的答案是:[0,-3,9,-10,null,5],它可以表示下面这个高度平衡二叉搜索树:
0
/ \
-3 9
/ /
-10 5

理论知识:

  • 二叉搜索树:左子树上的值都小于根节点的值,右子树上的值都大于根节点上的值,通过树的中序遍历,可以得到一个升序序列。
  • 高度最小:就是保持高度平衡,也就是左右子树的节点个数尽可能接近,也就是尽量把中间大小的值作为根节点

思路:定义三个变量

  • nums:代表序列数组
  • low:代表当前要考虑的子树的最左边
  • high:代表当前要考虑的子树的最右边

当low>high的时候,递归结束。

代码:

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; } // 带参构造方法
 * }
 */
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return dfs(nums, 0, nums.length-1);
    }
    public TreeNode dfs(int[] nums, int low, int high){
        if(low>high){
            return null;
        }
        int mid = (high - low) / 2 + low;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = dfs(nums, low, mid - 1);
        root.right = dfs(nums, mid + 1, high);
        return root;
    }
}

2.剑指 Offer 55 - I. 二叉树的深度

题目描述:

输入一棵二叉树的根节点,求该树的深度。从根节点到叶节点依次经过的节点(含根、叶节点)形成树的一条路径,最长路径的长度为树的深度。
例如:
给定二叉树 [3,9,20,null,null,15,7],
3
/
9 20
/
15 7
返回它的最大深度 3 。
提示:
节点总数 <= 10000

理论知识:

  • 后序遍历:先穷尽左子树,再穷尽右子树,最后回到根节点(使用递归或者栈实现)
  • 层序遍历:以二叉树的一层为单位,向下遍历(使用队列实现)

思路1:当前root为null时,表明当前子树的深度为0,无法对上层迭代起到任何贡献,return 0;否则计算左子树的深度以及右子树的深度,当前子树的深度等于max(左子树深度,右子树深度)+1。
代码1:

class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null){
            return 0;
        }else{
            return Math.max(maxDepth(root.left),maxDepth(root.right))+1;
        }
    }
}

思路2:定义一个队列,一开始存放root,每一次循环,深度+1。接着,首先记录队列中已有的root数,依次把这些root给pop掉,每pop掉一个root,都要add该root的left和right(如果不是null的话)。直到队列为空。

java知识点:

  • 创建链表对象:Queue<> q = new LinkedList<>();
  • 添加元素:q.add(…);
  • 移除顶部对象:q.poll();
class Solution {
    public int maxDepth(TreeNode root) {
        if(root==null) return 0;
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.add(root);
        int res = 0;
        while(!q.isEmpty()){
            res ++;
            int s = q.size();
            for(int i = 0;i<s;i++){
                TreeNode n = q.poll();
                if(n.left!=null) q.add(n.left);
                if(n.right!=null) q.add(n.right);
            }
        }
        return res;
    }
}

3.938. 二叉搜索树的范围和

题目描述:

给定二叉搜索树的根结点 root,返回值位于范围 [low, high] 之间的所有结点的值的和。
示例 1:
输入:root = [10,5,15,3,7,null,18], low = 7, high = 15
输出:32
示例 2:
输入:root = [10,5,15,3,7,13,18,1,null,6], low = 6, high = 10
输出:23
提示:
树中节点数目在范围 [1, 2 * 104] 内
1 <= Node.val <= 105
1 <= low <= high <= 105
所有 Node.val 互不相同

思路1:一种不懂脑筋的思路就是,遍历二叉搜索树的所有结点,如果结点的val在范围内,则加上。而遍历二叉搜索树的方法有DFS和层次遍历,代码1采用DFS
代码1:DFS

class Solution {
    public int rangeSumBST(TreeNode root, int low, int high) {
        if(root==null){
            return 0;
        }else{
            int sub_tree_ans = rangeSumBST(root.left, low, high) + rangeSumBST(root.right,low,high);
            return (low<=root.val&&root.val<=high)?(root.val+sub_tree_ans):sub_tree_ans;
            }
    }
}

思路2:采用层次遍历二叉树
代码2:

class Solution {
    public int rangeSumBST(TreeNode root, int low, int high) {
        if(root==null){
            return 0;
        }
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.add(root);
        int ans = 0;
        while(!q.isEmpty()){
            int s = q.size();
            for(int i = 0;i<s;i++){
                TreeNode n = q.poll();
                if(low<=n.val&&n.val<=high) ans += n.val;
                if(n.left!=null) q.add(n.left);
                if(n.right!=null) q.add(n.right);
            }
        }
        return ans;
    }
}

思路3:以上面的代码为例,root的左子树一定小于root.val,root的右子树一定大于root.val,所以,如果root.val已经小于等于low了,那么root的左子树就不需要加入了;如果root.val已经大于等于high了,那么root的右子树就不需要加入了。

//dfs
class Solution {
    public int rangeSumBST(TreeNode root, int low, int high) {
        if(root==null){
            return 0;
        }else{
            int sub_tree_ans = 0;
            if(root.val>low) sub_tree_ans += rangeSumBST(root.left, low, high);
            if(root.val<high) sub_tree_ans += rangeSumBST(root.right, low, high);
            return (low<=root.val&&root.val<=high)?(root.val+sub_tree_ans):sub_tree_ans;
            }
    }
}
//层次
class Solution {
    public int rangeSumBST(TreeNode root, int low, int high) {
        if(root==null){
            return 0;
        }
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.add(root);
        int ans = 0;
        while(!q.isEmpty()){
            int s = q.size();
            for(int i = 0;i<s;i++){
                TreeNode n = q.poll();
                if(low<=n.val&&n.val<=high) ans += n.val;
                if(n.left!=null&&n.val>low) q.add(n.left);
                if(n.right!=null&&n.val<high) q.add(n.right);
            }
        }
        return ans;
    }
}

4.104. 二叉树的最大深度

和"2.剑指 Offer 55 - I. 二叉树的深度"一样

5.108. 将有序数组转换为二叉搜索树

和"面试题 04.02 最小高度数"一样

6.897. 递增顺序查找树

题目描述:

给你一个树,请你 按中序遍历 重新排列树,使树中最左边的结点现在是树的根,并且每个结点没有左子结点,只有一个右子结点。

思路:按照中序遍历,并创建一个全局的父亲节点,每一次对root执行以下操作:root.left=null; 父亲节点.right=root;父亲节点=root。
代码:

class Solution {
    public TreeNode cur = new TreeNode();
    public TreeNode increasingBST(TreeNode root) {
        TreeNode ans = cur; // 理解为:ans和cur一开始共用一个地址,所以第一个cur.right=root同时也改变了ans.right; 然后cur=root,之后的ans和cur不再是同一个地址。
        dfs(root);
        return ans.right;
    }
    public void dfs(TreeNode root){
        if(root==null) return;
        dfs(root.left);
        root.left = null;
        cur.right = root; // ans.right = root;
        cur = root;
        dfs(root.right);
    }
}

7.559. N 叉树的最大深度

题目描述:

给定一个 N 叉树,找到其最大深度。
最大深度是指从根节点到最远叶子节点的最长路径上的节点总数。
N 叉树输入按层序遍历序列化表示,每组子节点由空值分隔(请参见示例)。

思路:dfs
代码:

class Solution {
    public int maxDepth(Node root) {
        return dfs(root);
    }
    public int dfs(Node root){
        if(root==null) return 0;
        int ans = 0;
        for(Node i : root.children){
            ans = Math.max(ans, dfs(i));
        }
        return ans + 1;
    }
}

8.257. 二叉树的所有路径

题目描述:

给定一个二叉树,返回所有从根节点到叶子节点的路径。
说明: 叶子节点是指没有子节点的节点。

代码1:使用String

class Solution {
    public List<String> ans_lst = new ArrayList<String>();
    public List<String> binaryTreePaths(TreeNode root) {
        if(root==null) return ans_lst;
        dfs(root, "");
        return ans_lst;
    }
    public void dfs(TreeNode root, String s){
        if(root==null) {
            return;
        }
        if(root.left==null&&root.right==null){
            s += root.val;
            ans_lst.add(s);
        }
        dfs(root.left, s+root.val+"->");
        dfs(root.right, s+root.val+"->");
    }
}

代码2:String进行"+"操作,需要O|S|的时间,所以采用StringBuilder

class Solution {
    public List<String> ans_lst = new ArrayList<String>();
    public List<String> binaryTreePaths(TreeNode root) {
        if(root==null) return ans_lst;
        StringBuilder sb = new StringBuilder();
        dfs(root, sb);
        return ans_lst;
    }
    public void dfs(TreeNode root, StringBuilder sb){
        if(root==null) {
            return;
        }
        if(root.left==null&&root.right==null){
            sb.append(root.val);
            ans_lst.add(sb.toString());
        }
        dfs(root.left, new StringBuilder(sb).append(root.val).append("->"));
        dfs(root.right, new StringBuilder(sb).append(root.val).append("->"));
    }
}

9. 872. 叶子相似的树

题目描述:

请考虑一棵二叉树上所有的叶子,这些叶子的值按从左到右的顺序排列形成一个 叶值序列 。

思路:前序、中序、后序都满足从左到右遍历叶子节点。数据结构使用ArrayList,使用StringBuilder第39个样例错了…
代码:

class Solution {
    public boolean leafSimilar(TreeNode root1, TreeNode root2) {
        List<Integer> leaves1 = new ArrayList();
        List<Integer> leaves2 = new ArrayList();
        dfs(root1,leaves1);
        dfs(root2,leaves2);
        return leaves1.equals(leaves2);
    }
    public void dfs(TreeNode root, List<Integer> leaves){
        if(root==null){
            return;
        }
        dfs(root.left,leaves);
        dfs(root.right,leaves);
        if(root.left==null&&root.right==null){
            leaves.add(root.val);
        }
    }
}

10. 100. 相同的树

题目描述:

给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。
如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。

代码:

class Solution {
    public boolean isSameTree(TreeNode p, TreeNode q) {
        return dfs(p,q);
    }
    public boolean dfs(TreeNode p,TreeNode q){
        if(p==null) return q==null;
        if(q==null) return p==null;
        if(p.val!=q.val){
            return false;
        }
        boolean ans_left = dfs(p.left,q.left);
        if(ans_left==false) return false;
        boolean ans_right = dfs(p.right,q.right);
        if(ans_right==false) return false;
        return true;
    }
}

11. 690. 员工的重要性

题目描述

给定一个保存员工信息的数据结构,它包含了员工唯一的id,重要度 和 直系下属的id。
比如,员工1是员工2的领导,员工2是员工3的领导。他们相应的重要度为15, 10, 5。那么员工1的数据结构是[1, 15, [2]],员工2的数据结构是[2, 10, [3]],员工3的数据结构是[3, 5, []]。注意虽然员工3也是员工1的一个下属,但是由于并不是直系下属,因此没有体现在员工1的数据结构中。
现在输入一个公司的所有员工信息,以及单个员工id,返回这个员工和他所有下属的重要度之和。

思路:首先为方便查找员工,建立一个哈希表。然后dfs。又由于题目中说:一个员工最多有一个直系领导,所以每一个员工最多只会被访问1次,下面的have_visit并不需要。
代码:

class Solution {
    public int[] have_visit = new int[2001];
    public int ans = 0;
    public Map<Integer, Employee> idx_map = new HashMap<Integer, Employee>();
    public int getImportance(List<Employee> employees, int id) {
        for(Employee e:employees){
            idx_map.put(e.id, e);
        }
        dfs(id);
        return ans;
    }
    public void dfs(int id){
        if(have_visit[id]==0){
            ans += idx_map.get(id).importance;
            have_visit[id]=1;
        }
        for(int i: idx_map.get(id).subordinates){
            if(have_visit[i]==0){
                dfs(i);
            }
        }
    }
}

12.563. 二叉树的坡度

题目描述:

给定一个二叉树,计算 整个树 的坡度 。
一个树的 节点的坡度 定义即为,该节点左子树的节点之和和右子树节点之和的 差的绝对值 。如果没有左子树的话,左子树的节点之和为 0 ;没有右子树的话也是一样。空结点的坡度是 0 。
整个树 的坡度就是其所有节点的坡度之和。

思路:遍历每一个节点,对于一个节点,首先计算它的左子树的和,然后计算它的右子树的和,然后更新答案,最后返回包括它在内的整棵子树的和。

class Solution {
    public int ans = 0;
    public int findTilt(TreeNode root) {
        dfs(root);
        return ans;
    }
    public int dfs(TreeNode root){
        if(root==null) return 0;
        int left_sum = dfs(root.left);
        int right_sum = dfs(root.right);
        ans += Math.abs(right_sum - left_sum);
        return root.val + right_sum + left_sum;
    }
}

13.剑指 Offer 55 - II. 平衡二叉树

题目描述:

输入一棵二叉树的根节点,判断该树是不是平衡二叉树。如果某二叉树中任意节点的左右子树的深度相差不超过1,那么它就是一棵平衡二叉树。

思路:和上一题的思路类似,遍历每一个节点,对于一个节点,首先计算它的左子树深度,然后计算它的右子树深度,然后更新答案,最后返回包括它在内的子树的深度(+1)。

代码:

class Solution {
    public boolean res = true;
    public boolean isBalanced(TreeNode root) {
        dfs(root);
        return res;
    }
    public int dfs(TreeNode root){
        if(root==null) return 0;
        int left_depth = dfs(root.left);
        int right_depth = dfs(root.right);
        if(Math.abs(left_depth-right_depth)>1) res=false;
        return Math.max(left_depth, right_depth) + 1;
    }
}

思路2:剪枝,在上面后序遍历的基础上,对于一个节点而言,先判断它的左子树是否平衡,再判断右子树是否平衡,最后判断它是否平衡,所以在返回结果中加入状态量-1,代表不平衡。
代码2:

class Solution {
    public boolean isBalanced(TreeNode root) {
        return dfs(root)!=-1;
    }
    public int dfs(TreeNode root){
        if(root==null) return 0;
        int left_depth = dfs(root.left);
        if(left_depth == -1) return -1;
        int right_depth = dfs(root.right);
        if(right_depth == -1) return -1;
        return Math.abs(left_depth-right_depth)>1?-1:Math.max(left_depth, right_depth) + 1;
    }
}

14.733. 图像渲染

题目描述:

有一幅以二维整数数组表示的图画,每一个整数表示该图画的像素值大小,数值在 0 到 65535 之间。
给你一个坐标 (sr, sc) 表示图像渲染开始的像素值(行 ,列)和一个新的颜色值 newColor,让你重新上色这幅图像。
为了完成上色工作,从初始坐标开始,记录初始坐标的上下左右四个方向上像素值与初始坐标相同的相连像素点,接着再记录这四个方向上符合条件的像素点与他们对应四个方向上像素值与初始坐标相同的相连像素点,……,重复该过程。将所有有记录的像素点的颜色值改为新的颜色值。
最后返回经过上色渲染后的图像。

思路:dfs
代码:👇。实际上have_visit并不需要,如果一个点被访问过,那由于val != newColor和image[n_sr][n_sc]==val的这两个判断,这个点的颜色肯定和它之前的颜色不一样,所以只要看颜色就能够直到它是否被访问过。

class Solution {
    public int[] dx = {-1,1,0,0};
    public int[] dy = {0,0,-1,1};
    public int[][] have_visit = new int[50][50];
    public int val;
    public int X;
    public int Y;
    public int[][] floodFill(int[][] image, int sr, int sc, int newColor) {
        val = image[sr][sc];
        X = image.length;
        Y = image[0].length;
        if(val != newColor){
            dfs(image, sr, sc,newColor);
        }
        return image;
    }
    public void dfs(int[][] image, int sr, int sc, int newColor){
        image[sr][sc] =  newColor;
        have_visit[sr][sc] = 1;
        for(int i = 0; i < 4; i++){
            int n_sr = sr + dx[i];
            int n_sc = sc + dy[i];
            if(check(image,n_sr,n_sc)){
                dfs(image, n_sr, n_sc, newColor);
            }
        }
    }
    public boolean check(int[][] image, int n_sr, int n_sc){
        return ((0<=n_sr&&n_sr<X&&0<=n_sc&&n_sc<Y)&&(have_visit[n_sr][n_sc]==0)&&(image[n_sr][n_sc]==val));
    }
}

15.面试题 04.04. 检查平衡性

和剑指 Offer 55 - II. 平衡二叉树一样。

16. 783. 二叉搜索树节点最小距离

题目描述:

给定一个二叉搜索树的根节点 root,返回树中任意两节点的差的最小值。

思路:按照中序遍历,保存当前的root和上一个的root(pre),root一定比pre大。
代码:

class Solution {
    int res = 100;
    TreeNode pre = null;
    public int minDiffInBST(TreeNode root) {
        dfs(root);
        return res;
    }
    public void dfs(TreeNode root){
        if(root == null) return;
        dfs(root.left);
        if(pre!=null) res = Math.min(res, root.val - pre.val);
        pre = root;
        dfs(root.right);
    }
}

17. 110. 平衡二叉树

题目描述:

给定一个二叉树,判断它是否是高度平衡的二叉树。

代码:

class Solution {
    public boolean isBalanced(TreeNode root) {
        return dfs(root)!=-1;
    }
    public int dfs(TreeNode root){
        if(root==null) return 0;
        int left_height = dfs(root.left);
        if(left_height == -1) return -1;
        int right_height = dfs(root.right);
        if(right_height == -1) return -1;
        return Math.abs(left_height - right_height)>1?-1:Math.max(left_height, right_height) + 1;
    }
}

18.面试题 08.10. 颜色填充

和“733. 图像渲染”一样

19.101. 对称二叉树

题目描述:

给定一个二叉树,检查它是否是镜像对称的。

思路:定义左右指针。
代码:

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return dfs(root, root);
    }
    public boolean dfs(TreeNode l_root, TreeNode r_root){
        if(l_root==null && r_root == null) return true;
        if(l_root==null || r_root == null) return false;
        return l_root.val == r_root.val && dfs(l_root.left, r_root.right) && dfs(l_root.right, r_root.left);
    }
}

20.112. 路径总和

题目描述:

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum ,判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。
叶子节点 是指没有子节点的节点。

思路:只有在当前节点是叶子节点的时候,才需要判断是否成立。
代码:

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        return dfs(root, targetSum, 0);
    }
    public boolean dfs(TreeNode root, int targetSum, int pathSum){
        if(root==null) return false;
        if(root.left == null && root.right == null) 
        {
            return pathSum+root.val==targetSum;
        }
        return dfs(root.left, targetSum, pathSum + root.val) || dfs(root.right, targetSum, pathSum + root.val);
    }
}

21.111. 二叉树的最小深度

题目描述:

给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。

思路:先自底向上计算每一个节点的最小深度,但需要注意一点:当一个节点,它的左孩子和右孩子都是null的时候,才算是叶子节点,此时它的最小深度是0+0+1=1;如果仅有它的左孩子或者它的右孩子是null的时候,它的最小深度不应该是0,而应该是非null那一边的子树的深度+1。
代码:

class Solution {
    public int minDepth(TreeNode root) {
        return dfs(root);
    }
    public int dfs(TreeNode root){
        if(root == null) return 0;
        int left_depth = dfs(root.left);
        int right_depth = dfs(root.right);
        return (left_depth==0||right_depth==0)?left_depth+right_depth+1:Math.min(left_depth,right_depth)+1;
    }
}

over

中等题

1. 1379. 找出克隆二叉树中的相同节点

题目描述:

给你两棵二叉树,原始树 original 和克隆树 cloned,以及一个位于原始树 original 中的目标节点 target。
其中,克隆树 cloned 是原始树 original 的一个 副本 。
请找出在树 cloned 中,与 target 相同 的节点,并返回对该节点的引用(在 C/C++ 等有指针的语言中返回 节点指针,其他语言返回节点本身)。
注意:
你 不能 对两棵二叉树,以及 target 节点进行更改。
只能 返回对克隆树 cloned 中已有的节点的引用。

思路1:dfs(但似乎是前序遍历~)
代码1:

class Solution {
    public final TreeNode getTargetCopy(final TreeNode original, final TreeNode cloned, final TreeNode target) {
        return dfs(cloned, target.val);
    }
    public TreeNode dfs(TreeNode cloned, int val){
        if(cloned == null) return null;
        if(cloned.val == val) return cloned;
        TreeNode ans_left = dfs(cloned.left, val);
        if(ans_left!=null) return ans_left;
        TreeNode ans_right = dfs(cloned.right, val);
        if(ans_right!=null) return ans_right;
        return null;
    }
}

思路2:上面的代码无法解决"进阶:如果树中允许出现值相同的在这里插入代码片节点,你将如何解答"的问题,所以需要同时遍历两棵树。
代码2:

class Solution {
    public final TreeNode getTargetCopy(final TreeNode original, final TreeNode cloned, final TreeNode target) {
        if(original==null) return null;
        if(original==target) return cloned;
        TreeNode ans_left = getTargetCopy(original.left,cloned.left,target);
        if(ans_left!=null) return ans_left;
        TreeNode ans_right = getTargetCopy(original.right,cloned.right,target);
        if(ans_right!=null) return ans_right;
        return null;
    }
}

2.1302. 层数最深叶子节点的和

题目描述:

给你一棵二叉树,请你返回层数最深的叶子节点的和。

思路:也是遍历整棵树,如果用层次遍历,那只要计算每一层的和,直到while截止,最后那一层的和就是答案;如果用深度优先搜索,需要维护max_depth和total两个全局变量,和depth这个局部变量。 如果当前节点的depth>max_depth,则需要更新max_depth,并重新初始化total为当前节点的val;如果当前节点的depth=max_depth,则更新total;如果<,则啥也不需要做。 不管大小关系如何,都要继续递归下去。

class Solution {
    int max_depth = -1;
    int total = 0;
    public int deepestLeavesSum(TreeNode root) {
        dfs(root, 0);
        return total;
    }
    public void dfs(TreeNode root, int depth){
        if(root == null) return;
        if(depth>max_depth){
            max_depth = depth;
            total = root.val;
        }else if(depth == max_depth){
            total += root.val;
        }
        dfs(root.left, depth+1);
        dfs(root.right, depth+1);
    }
}

3.1315. 祖父节点值为偶数的节点和

题目描述:

给你一棵二叉树,请你返回满足以下条件的所有节点的值之和:
该节点的祖父节点的值为偶数。(一个节点的祖父节点是指该节点的父节点的父节点。)
如果不存在祖父节点值为偶数的节点,那么返回 0 。

思路1:依然是遍历二叉树,但需要维护三个状态:祖父、父亲、你。
代码1:

class Solution {
    public int ans = 0;
    public int sumEvenGrandparent(TreeNode root) {
        if(root.left!=null){
            dfs(root,root.left,root.left.left);
            dfs(root,root.left,root.left.right);
        }
        if(root.right!=null){
            dfs(root,root.right,root.right.left);
            dfs(root,root.right,root.right.right);
        }
        return ans;
    }
    public void dfs(TreeNode g,TreeNode f,TreeNode y){
        if(y == null) return;
        if(g.val % 2 == 0) ans += y.val;
        dfs(f,y,y.left);
        dfs(f,y,y.right);
    }
}

思路2:增加虚节点使代码更简洁
代码2:

class Solution {
    public int ans = 0;
    public int sumEvenGrandparent(TreeNode root) {
        dfs(3,3,root);
        return ans;
    }
    public void dfs(int g, int p, TreeNode y){
        if(y==null) return;
        if(g % 2 == 0) ans += y.val;
        dfs(p, y.val, y.left);
        dfs(p, y.val, y.right);
    }
}

4. 1038. 把二叉搜索树转换为累加树

题目描述:

给出二叉 搜索 树的根节点,该树的节点值各不相同,请你将其转换为累加树(Greater Sum Tree),使每个节点 node 的新值等于原树中大于或等于 node.val 的值之和。

思路1:一开始想的是,先进行一次"看":中序遍历得到递增数列,然后反向遍历该数列进行累加更新;再进行一次"改"。
代码1:

class Solution {
    public final int maxn = 101;
    public int[] val_lst = new int[maxn];
    public int cot = -1;
    public TreeNode bstToGst(TreeNode root) {
        look(root);
        sum();
        cot = -1;
        return change(root);
    }
    public void look(TreeNode root){
        if(root==null) return;
        look(root.left);
        cot ++;
        val_lst[cot] = root.val;
        look(root.right);
    }
    public void sum(){
        int node_num = cot + 1;
        int tmp_sum = 0;
        for(int i = node_num - 1; i >=0; i--){
            int tmp_val = val_lst[i];
            val_lst[i] += tmp_sum;
            tmp_sum += tmp_val; //这里有点蠢
        }
    }
    public TreeNode change(TreeNode root){
        if(root==null) return null;
        change(root.left);
        cot ++;
        root.val = val_lst[cot];
        change(root.right);
        return root;
    }
}

思路2:参考解答:逆中序遍历~(但似乎时间和空间上都没有多大差别)

class Solution {
    int tmp_sum = 0;
    public TreeNode bstToGst(TreeNode root) {
        if(root==null) return null;
        bstToGst(root.right);
        root.val += tmp_sum;
        tmp_sum = root.val;
        bstToGst(root.left);
        return root;
    }
}

5. 109. 有序链表转换二叉搜索树

给定一个单链表,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1。

理论知识:

  • 快慢指针法:对于一个链表,定义一个快指针和慢指针,每一次快指针走两步,慢指针走一步,快指针停的时候,慢指针指向的就是中心。
  • 为什么要左闭右开:单向链表中,访问前驱是不方便的,所以在找到mid的时候,把mid作为左子树的right,并将其看作一个虚元素。

思路1:采用前序遍历构造二叉搜索树,即首先找到root,然后构造左子树,再构造右子树。
代码1:

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        return dfs(head, null);
    }
    public TreeNode dfs(ListNode left,ListNode right){
        if(left==right) return null;
        ListNode mid = get_mid(left,right);
        TreeNode root = new TreeNode(mid.val);
        root.left = dfs(left, mid);
        root.right = dfs(mid.next, right);
        return root;
    }
    public ListNode get_mid(ListNode left, ListNode right){
        ListNode slow = left;
        ListNode fast = left;
        while(fast!=right&&fast.next!=right){
            fast = fast.next;
            fast = fast.next;
            slow = slow.next;
        }
        return slow;
    }
}

思路2:中序遍历
代码2:

class Solution {
    public ListNode globalhead;
    public TreeNode sortedListToBST(ListNode head) {
        globalhead = head;
        int length = get_length(head);
        return dfs(0, length-1);
    }
    public TreeNode dfs(int low, int high){
        if(low>high) return null;
        int mid = (high-low)/2 + low;
        TreeNode root = new TreeNode();
        root.left = dfs(low, mid - 1);
        root.val = globalhead.val;
        globalhead = globalhead.next;
        root.right = dfs(mid+1, high);
        return root;
    }
    public int get_length(ListNode head){
        int cot = 0;
        while(head!=null){
            cot ++;
            head = head.next;
        }
        return cot;
    }
}

6. 959. 由斜杠划分区域

题目描述:

在由 1 x 1 方格组成的 N x N 网格 grid 中,每个 1 x 1 方块由 /、\ 或空格构成。这些字符会将方块划分为一些共边的区域。
(请注意,反斜杠字符是转义的,因此 \ 用 “\” 表示。)。
返回区域的数目。

  • java知识点:char[] …= String.toCharArray();

思路:初始化NxN个小方格,每一个小方格被看作是4个对角上的三角形,编号0,1,2,3。对于每一个方格,根据对应的符号,进行单元格内合并 和 单元格间合并两个操作,一次合并的成功会减少一个不连通区域。 合并操作采用并查集。
代码:

class Solution {
    public final int maxn = 4*30*30;
    public int regionsBySlashes(String[] grid) {
        int N = grid.length;
        int size = 4*N*N;
        Bcg bcg = new Bcg(size);
        for(int i = 0; i < N; i++){
            char[] row = grid[i].toCharArray();
            for(int j = 0; j < N; j++){
                char c = row[j];
                int index = 4*(i*N+j);
                if(c == ' '){
                    bcg.union(index, index+1);
                    bcg.union(index+1, index+2);
                    bcg.union(index+2, index+3);
                }else if(c == '/'){
                    bcg.union(index, index+3);
                    bcg.union(index+1, index+2);
                }else if(c == '\\'){
                    bcg.union(index, index+1);
                    bcg.union(index+2, index+3);
                }
                if(i!=N-1) bcg.union(index+2, 4*((i+1)*N+j));
                if(j!=N-1) bcg.union(index+1, 4*(i*N+j+1)+3);
            }
        }
        return bcg.get_cot();
    }
    public class Bcg{
        private int cot;
        private int[] parents = new int[maxn];
        public Bcg(int size){
            cot = size;
            for(int i = 0;i<parents.length;i++){
                parents[i] = i;
            }
        }
        public void union(int i, int j){
            int root_i = find_root(i);
            int root_j = find_root(j);
            if(root_i==root_j) return;
            parents[root_i] = root_j;
            cot --;
        }
        public int find_root(int i){
            if(parents[i] != i){
                parents[i] = find_root(parents[i]);
            }
            return parents[i];
        }
        public int get_cot(){
            return cot;
        }
    }
}

7. 797. 所有可能的路径

题目描述:

给一个有 n 个结点的有向无环图,找到所有从 0 到 n-1 的路径并输出(不要求按顺序)
二维数组的第 i 个数组中的单元都表示有向图中 i 号结点所能到达的下一些结点(译者注:有向图是有方向的,即规定了 a→b 你就不能从 b→a )空就是没有下一个结点了。

思路:深度优先搜索,遇到当前节点(已经被加入ArrayList的末尾)==n-1时,将当前ArrayList加入答案。但java用的有点丑。
代码:

class Solution {
    public List<List<Integer>> ans_lst = new ArrayList();
    public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
        ArrayList<Integer> now_lst = new ArrayList();
        now_lst.add(0);
        dfs(graph, now_lst, graph.length);
        return ans_lst;
    }
    public void dfs(int[][] graph, ArrayList<Integer> now_lst, int n){
        if(now_lst.get(now_lst.size()-1) == n-1) {
            ans_lst.add(now_lst);
            return;
        }
        for(int i : graph[now_lst.get(now_lst.size()-1)]){
            ArrayList<Integer> new_lst= new ArrayList(now_lst);
            new_lst.add(i);
            dfs(graph, new_lst,n);
        }
    }
}

代码(改进):

class Solution {
    public List<List<Integer>> allPathsSourceTarget(int[][] graph) {
        return solve(graph, 0);
    }

    public List<List<Integer>> solve(int[][] graph, int node) {
        int N = graph.length;
        List<List<Integer>> ans = new ArrayList();
        if (node == N - 1) {
            List<Integer> path = new ArrayList();
            path.add(N-1);
            ans.add(path);
            return ans;
        }

        for (int nei: graph[node]) {
            for (List<Integer> path: solve(graph, nei)) {
                path.add(0, node);
                ans.add(path);
            }
        }
        return ans;
    }
}

8. 513. 找树左下角的值

题目描述:

给定一个二叉树,在树的最后一行找到最左边的值。

思路:不管怎样遍历,最后一行的最左边的值,就是最深的节点中,第一个被访问到的。所以:维护两个全局变量,1.max_depth 2.ans,每当我们遇到一个更深的节点,更新这两个变量。

代码:

class Solution {
    public int max_depth = -1;
    public int ans = -1;
    public int findBottomLeftValue(TreeNode root) {
        dfs(root, 0);
        return ans;
    }
    public void dfs(TreeNode root, int depth){
        if(root==null) return;
        dfs(root.left, depth+1);
        if(depth>max_depth){
            max_depth = depth;
            ans = root.val;
        }
        dfs(root.right,depth+1);
    }
}

9. 114. 二叉树展开为链表

题目描述:

给你二叉树的根结点 root ,请你将它展开为一个单链表:
展开后的单链表应该同样使用 TreeNode ,其中 right 子指针指向链表中下一个结点,而左子指针始终为 null 。
展开后的单链表应该与二叉树 先序遍历 顺序相同。

思路1:本打算和可以和897. 递增顺序查找树一样的做法,但是下面的代码不对…暂时不知道为何。

class Solution {
    public TreeNode cur = new TreeNode();
    public void flatten(TreeNode root) {
        TreeNode ans = cur;
        dfs(root);
        root = ans.right;
    }
    public void dfs(TreeNode root){
        if(root==null) return;
        TreeNode left = root.left;
        root.left = null;
        cur.right = root;
        cur = root;
        dfs(left);
        dfs(root.right);
    }
}

思路2:首先look,再create
思路3:看作是寻找先驱的问题。即在先序遍历中,对于一个root,首先访问root,然后访问左子树,当访问完它的左子树的最右边的节点,接下去就要访问右子树的第一个节点。所以对于每一个root,找到其左子树的最右边的节点,设置该节点的右子节点为root的右子节点,并把root的左子节点设置成空,右子节点设置成原来的左子节点。

class Solution {
    public void flatten(TreeNode root) {
        while(root!=null){
            if(root.left!=null){
                TreeNode next = root.left;
                TreeNode right_pre = root.left;
                while(right_pre.right!=null){
                    right_pre = right_pre.right;
                }
                right_pre.right = root.right;
                root.left = null;
                root.right = next;
            }
            root = root.right;
        }
    }
}

10. 106. 从中序与后序遍历序列构造二叉树

题目描述:

根据一棵树的中序遍历与后序遍历构造二叉树。

思路:待补充
代码:

class Solution {
    Map<Integer, Integer> idx_map = new HashMap<Integer,Integer>();
    public int[] inorder;
    public int[] postorder;
    public int post_index;
    public TreeNode buildTree(int[] inorder, int[] postorder) {
        this.inorder = inorder;
        this.postorder = postorder;
        post_index = postorder.length-1;
        int idx = 0;
        for(int i : inorder){
            idx_map.put(i, idx++);
        }
        return dfs(0, inorder.length-1);
    }
    public TreeNode dfs(int low, int high){
        if(low>high) return null;
        int root_val = postorder[post_index];
        TreeNode root = new TreeNode(root_val);
        int mid_index = idx_map.get(root_val);
        post_index--;
        root.right = dfs(mid_index +1, high);
        root.left = dfs(low, mid_index - 1);
        return root;
    }
}

11.1448. 统计二叉树中好节点的数目

题目描述:

给你一棵根为 root 的二叉树,请你返回二叉树中好节点的数目。
「好节点」X 定义为:从根到该节点 X 所经过的节点中,没有任何节点的值大于 X 的值。

思路:对于每一条路径,维护一个maxn,当当前节点>=maxn时,表明该节点在目前的路径上是最大的,更新结果,同时更新maxn。

代码:

class Solution {
    public int goodNodes(TreeNode root) {
        int maxn = (int)-Math.pow(10,4);
        return dfs(root, maxn);
    }
    public int dfs(TreeNode root, int maxn){
        if(root==null) return 0;
        int cot = 0;
        if(root.val>=maxn){
            maxn = root.val;
            cot ++;
        }
        cot += dfs(root.left, maxn);
        cot += dfs(root.right, maxn);
        return cot;
    }
}

12.131. 分割回文串

题目描述:

给定一个字符串 s,将 s 分割成一些子串,使每个子串都是回文串。
返回 s 所有可能的分割方案。

思路:首先进行预处理:dp[j][i]代表从j开始到i结束的子串。递归时,从头遍历当前子串,截1个字符、2个、3个…。注意path,放入当前是回文的子串,进行递归,只要递归结束,就也一定进行过add和remove这两个操作,所以只需要再把当前放入的remove掉。
代码:

class Solution {
    public boolean[][] dp;
    public List<List<String>> res;
    public List<List<String>> partition(String s) {
        //预处理
        int len = s.length();
        dp = new boolean[len][len];
        for(int i=0;i<len;i++){
            for(int j=0;j<=i;j++){
                if((s.charAt(i)==s.charAt(j))&&(((i-j)<=2)||(dp[j+1][i-1]))){
                    dp[j][i] = true;
                }
            }
        }
        res = new ArrayList<>();
        Deque<String> path = new ArrayDeque<>();
        dfs(s, 0, s.length()-1, path);
        return res;
    }
    public void dfs(String s,int i, int j, Deque<String> path){
        if(i>j){
            res.add(new ArrayList<>(path));
        }
        for(int x=i;x<=j;x++){
            if(dp[i][x]==true){
                path.addLast(s.substring(i,x+1));
                dfs(s, x+1, j, path);
                path.removeLast();
            }
        }
    }
}

13.979. 在二叉树中分配硬币

题目描述:

给定一个有 N 个结点的二叉树的根结点 root,树中的每个结点上都对应有 node.val 枚硬币,并且总共有 N 枚硬币。
在一次移动中,我们可以选择两个相邻的结点,然后将一枚硬币从其中一个结点移动到另一个结点。(移动可以是从父结点到子结点,或者从子结点移动到父结点。)。
返回使每个结点上只有一枚硬币所需的移动次数。

思路:太难了我靠!!!对于一个节点,首先计算它的左子树和右子树需要多少个金币,然后记录所有需要转移的金币。最后更新当前节点还剩下的金币数=root.val+l+r,并告诉当前节点的父亲节点,我需要多少个金币:return root.val + l + r - 1。
代码:

class Solution {
    int res = 0;
    public int distributeCoins(TreeNode root) {
        dfs(root);
        return res;
    }
    public int dfs(TreeNode root){
        if(root==null) return 0; //空结点不需要拿走
        int l = dfs(root.left);
        res += Math.abs(l);
        int r = dfs(root.right);
        res += Math.abs(r);
        return root.val + l + r - 1;
    }
}

14.1123. 最深叶节点的最近公共祖先

题目描述:

给你一个有根节点的二叉树,找到它最深的叶节点的最近公共祖先。
回想一下:
叶节点 是二叉树中没有子节点的节点
树的根节点的 深度 为 0,如果某一节点的深度为 d,那它的子节点的深度就是 d+1
如果我们假定 A 是一组节点 S 的 最近公共祖先,S 中的每个节点都在以 A 为根节点的子树中,且 A 的深度达到此条件下可能的最大值。

理论知识:

  • 最深的叶节点的最近公共祖先:当最深的叶节点分布在一个节点的左右子树中,则该节点就是。问题一:有没有比该节点更深的?—没有,因为更深意味着在该节点的左右子树上,也就是说更深的节点的后代中仅包含部分最深的叶节点,所以它不可能成为最深的叶节点的公共祖先。 问题二:有没有比该节点更浅的?—也没有,因为更浅意味着是该节点的祖先,也就是说更浅的节点的左子树或者右子树包含了所有的最深的叶结点,而此时该节点的左右子树深度一定不一样。
  • 深度:深度是越往下越深,但这题中讨论的一个节点的深度,是指该节点所在子树中最深节点的深度。

思路:先dfs得到所有节点的深度,再dfs寻找左右子树深度相同的根,如果左子树深度深,则向左遍历,否则向右遍历。
代码1:

class Solution {
    public Map<TreeNode, Integer> depth_map = new HashMap<>();
    public TreeNode lcaDeepestLeaves(TreeNode root) {
        get_max_depth(root);
        return dfs(root);
    }
    public int get_max_depth(TreeNode root){
        if(root == null) {
            depth_map.put(root, 0);
            return 0;
        }
        int max_depth_left = get_max_depth(root.left);
        int max_depth_right = get_max_depth(root.right);
        int max_depth = Math.max(max_depth_left,max_depth_right)+1;
        depth_map.put(root, max_depth);
        return max_depth;
    }
    public TreeNode dfs(TreeNode root){
        if(root==null) return null;
        int left_depth = depth_map.get(root.left);
        int right_depth = depth_map.get(root.right);
        if(left_depth == right_depth){
            return root;
        }else if(left_depth > right_depth){
            return dfs(root.left);
        }else{
            return dfs(root.right);
        }
    }
}

代码2:统计深度和递归同时进行,目前还不是特别懂

class Ans{
    TreeNode node;
    int depth;
    Ans(TreeNode node, int depth){
        this.node = node;
        this.depth = depth;
    }
}

class Solution {
    public TreeNode lcaDeepestLeaves(TreeNode root) {
        return dfs(root).node;
    }
    public Ans dfs(TreeNode root){
        if(root==null) return new Ans(null,0);
        Ans left_ans = dfs(root.left);
        Ans right_ans = dfs(root.right);
        if(left_ans.depth == right_ans.depth){
            return new Ans(root, left_ans.depth+1);
        }else if(left_ans.depth > right_ans.depth){
            return new Ans(left_ans.node, left_ans.depth+1);
        }else{
            return new Ans(right_ans.node, right_ans.depth+1);
        }
    }
}

15. 105. 从前序与中序遍历序列构造二叉树

题目描述:

根据一棵树的前序遍历与中序遍历构造二叉树。

和106. 从中序与后序遍历序列构造二叉树差不多。
代码:

class Solution {
    Map<Integer, Integer> idx_map = new HashMap<>();
    int[] preorder;
    int pre_index = 0;
    public TreeNode buildTree(int[] preorder, int[] inorder) {
        this.preorder = preorder;
        int idx = 0;
        for(int i:inorder){
            idx_map.put(i, idx);
            idx ++;
        }
        return dfs(0, inorder.length-1);
    }
    public TreeNode dfs(int low, int high){
        if(low>high) return null;
        int mid = preorder[pre_index];
        pre_index ++;
        TreeNode root = new TreeNode(mid);
        int mid_idx = idx_map.get(mid);
        root.left = dfs(low, mid_idx - 1);
        root.right = dfs(mid_idx+1,high);
        return root;
    }
}

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

题目描述:

给定一个 完美二叉树 ,其所有叶子节点都在同一层,每个父节点都有两个子节点。二叉树定义如下:struct Node {
int val;
Node *left;
Node *right;
Node *next;
}
填充它的每个 next 指针,让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点,则将 next 指针设置为 NULL。
初始状态下,所有 next 指针都被设置为 NULL。

思路1:用dfs中序遍历二叉树,保存在ArrayList中,规律就是,需要depth次循环(i=0,1,…depth-1),每一次的开始节点是2的i次方-1,每一次的步长是2的i+1次方。O(2n)…

代码1:

class Solution {
    List<Node> node_lst = new ArrayList();
    public Node connect(Node root) {
        int depth = dfs(root);
        for(int i = 0; i < depth; i++){
            int start = (int)Math.pow(2,i);
            int step = start * 2;
            start -= 1;
            while(true){
                if(start+step >= node_lst.size()){
                    // node_lst.get(start).next = null;
                    break;
                }
                node_lst.get(start).next = node_lst.get(start+step);
                start += step;
            }
        }
        return root;
    }
    public int dfs(Node root){
        if(root==null) return 0;
        int l = dfs(root.left);
        node_lst.add(root);
        int r = dfs(root.right);
        return l + 1;
    }
}

思路2:层次遍历,连接当前层。
代码2:

class Solution {
    public Node connect(Node root) {
        if(root == null) return null;
        Queue<Node> lst = new LinkedList();
        lst.add(root);
        while(!lst.isEmpty()){
            int s = lst.size();
            for(int i = 0; i < s;i++){
                Node node = lst.poll();
                if(i != s-1) node.next = lst.peek();
                if(node.left!=null) lst.add(node.left);
                if(node.right!=null) lst.add(node.right);
            }
        }
        return root;
    }
}

思路3:层次遍历,当前连接下一层,当前层已经连接。
代码3:

class Solution {
    public Node connect(Node root) {
        if(root==null) return null;
        Node leftmost = root;
        while(leftmost.left!=null){
            Node copy_root = leftmost;
            while(copy_root!=null){
                copy_root.left.next = copy_root.right;
                if(copy_root.next!=null){
                copy_root.right.next = copy_root.next.left;
                }
                copy_root = copy_root.next;
            }
            leftmost = leftmost.left;
        }
        return root;
    }
}

17. 129. 求根到叶子节点数字之和

题目描述:

给定一个二叉树,它的每个结点都存放一个 0-9 的数字,每条从根到叶子节点的路径都代表一个数字。
例如,从根到叶子节点路径 1->2->3 代表数字 123。
计算从根到叶子节点生成的所有数字之和。
说明: 叶子节点是指没有子节点的节点。

思路1:用StringBuilder做的…
代码1:

class Solution {
    public int sumNumbers(TreeNode root) {
        if(root==null) return 0;
        return dfs(root, new StringBuilder(String.valueOf(root.val)));
    }
    public int dfs(TreeNode root, StringBuilder sb){
        if(root.left==null && root.right==null){
            return Integer.parseInt(sb.toString());
        }
        int res = 0;
        if(root.left!=null){
            StringBuilder newsb1 = new StringBuilder(sb);
            newsb1.append(root.left.val);
            res += dfs(root.left, newsb1);
        }
        if(root.right!=null){
            StringBuilder newsb2 = new StringBuilder(sb);
            newsb2.append(root.right.val);
            res += dfs(root.right, newsb2);
        }
        return res;
    }
}

思路2:当前节点的值=上一个节点的值*10+当前节点的值
代码2:

class Solution {
    public int sumNumbers(TreeNode root) {
        if(root==null) return 0;
        return dfs(root, 0);
    }
    public int dfs(TreeNode root, int pre_sum){
        if(root==null) return 0;
        int sum = pre_sum*10 + root.val;
        if(root.left==null && root.right==null){
            return sum;
        }else{
            return dfs(root.left, sum) + dfs(root.right, sum);
        }
    }
}

18.133. 克隆图

题目描述:

给你无向 连通 图中一个节点的引用,请你返回该图的 深拷贝(克隆)。
图中的每个节点都包含它的值 val(int) 和其邻居的列表(list[Node])。

思路:深度优先搜索,每一个copy节点只会被new一次,new完之后,以值的形式保存在has_map中。对于一个新的节点,如果它的一个邻接节点之前已经被new过了,那么直接add那个先前的节点,而不会继续产生递归。
代码:

class Solution {
    public Map<Node, Node> has_map = new HashMap();
    public Node cloneGraph(Node node) {
        return dfs(node);

    }
    public Node dfs(Node root){
        if(root == null) return null;
        Node copy_root = new Node(root.val);
        has_map.put(root, copy_root);
        for(Node neighbor:root.neighbors){
            if(has_map.containsKey(neighbor)){
                copy_root.neighbors.add(has_map.get(neighbor));
            }else{
                copy_root.neighbors.add(dfs(neighbor));
            }
        }
        return copy_root;
    }
}

19.538. 把二叉搜索树转换为累加树

和"1038. 把二叉搜索树转换为累加树"一样。

20.841. 钥匙和房间

题目描述:

有 N 个房间,开始时你位于 0 号房间。每个房间有不同的号码:0,1,2,…,N-1,并且房间里可能有一些钥匙能使你进入下一个房间。
在形式上,对于每个房间 i 都有一个钥匙列表 rooms[i],每个钥匙 rooms[i][j] 由 [0,1,…,N-1] 中的一个整数表示,其中 N = rooms.length。 钥匙 rooms[i][j] = v 可以打开编号为 v 的房间。
最初,除 0 号房间外的其余所有房间都被锁住。
你可以自由地在房间之间来回走动。
如果能进入每个房间返回 true,否则返回 false。

思路:dfs
代码:

class Solution {
    boolean[] vis;
    int num = 0;
    public boolean canVisitAllRooms(List<List<Integer>> rooms) {
        vis = new boolean[rooms.size()];
        dfs(rooms, 0);
        return num == rooms.size();
    }
    public void dfs(List<List<Integer>> rooms, int now_in){
        vis[now_in] = true;
        num ++;
        for(int i: rooms.get(now_in)){
            if(!vis[i]){
                dfs(rooms, i);
            }
        }
    }
}

21.529. 扫雷游戏

题目描述:

让我们一起来玩扫雷游戏!
给定一个代表游戏板的二维字符矩阵。 ‘M’ 代表一个未挖出的地雷,‘E’ 代表一个未挖出的空方块,‘B’ 代表没有相邻(上,下,左,右,和所有4个对角线)地雷的已挖出的空白方块,数字(‘1’ 到 ‘8’)表示有多少地雷与这块已挖出的方块相邻,‘X’ 则表示一个已挖出的地雷。
现在给出在所有未挖出的方块中(‘M’或者’E’)的下一个点击位置(行和列索引),根据以下规则,返回相应位置被点击后对应的面板:
如果一个地雷(‘M’)被挖出,游戏就结束了- 把它改为 ‘X’。
如果一个没有相邻地雷的空方块(‘E’)被挖出,修改它为(‘B’),并且所有和其相邻的未挖出方块都应该被递归地揭露。
如果一个至少与一个地雷相邻的空方块(‘E’)被挖出,修改它为数字(‘1’到’8’),表示相邻地雷的数量。
如果在此次点击中,若无更多方块可被揭露,则返回面板。

思路:dfs+模拟(?)
代码:

lass Solution {
    public int[] dx = {-1, -1, 0, 1, 1, 1, 0, -1};
    public int[] dy = {0,   1, 1, 1, 0, -1,-1,-1};
    public int width;
    public int len;
    public char[][] updateBoard(char[][] board, int[] click) {
        width = board.length;
        len = board[0].length;
        if(board[click[0]][click[1]] == 'M'){
            board[click[0]][click[1]] = 'X';
            return board;
        }
        dfs(board, click);
        return board;
    }
    public void dfs(char[][] board, int[] click){
        int mine_num = 0;
        for(int i=0;i<dx.length;i++){
            int nx = click[0]+dx[i];
            int ny = click[1]+dy[i];
            if(0<=nx&&nx<width&&0<=ny&&ny<len){
                if(board[nx][ny] == 'M'){
                    mine_num ++;
                }
            }
        }
        if(mine_num == 0){
            board[click[0]][click[1]] = 'B';
            for(int i=0;i<dx.length;i++){
                int nx = click[0]+dx[i];
                int ny = click[1]+dy[i];
                if(0<=nx&&nx<width&&0<=ny&&ny<len){
                    if(board[nx][ny] == 'E'){
                        dfs(board, new int[] {nx, ny});
                    }
                }
            }
        }else{
            board[click[0]][click[1]] = (char)(mine_num+'0'); //(mine_num+'0')自动转换:char转换成了int
        }
    }
}

22. 1026. 节点与其祖先之间的最大差值

题目描述:

给定二叉树的根节点 root,找出存在于 不同 节点 A 和 B 之间的最大值 V,其中 V = |A.val - B.val|,且 A 是 B 的祖先。
(如果 A 的任何子节点之一为 B,或者 A 的任何子节点是 B 的祖先,那么我们认为 A 是 B 的祖先)

思路:对于每一条路径,都维护两个局部变量:min和max,当root==null时,表明当前路径结束,此时更新可能会被更新的答案.
代码:

class Solution {
    int global_max = 0;
    public int maxAncestorDiff(TreeNode root) {
        dfs(root, root.val, root.val);
        return global_max;
    }
    public void dfs(TreeNode root, int min, int max){
        if(root==null){
            global_max = Math.max(max-min, global_max);
            return;
        }
        if(root.val < min){
            min = root.val;
        }
        if(root.val > max){
            max = root.val;
        }
        dfs(root.left, min, max);
        dfs(root.right, min, max);
    }
}

23. 199. 二叉树的右视图

题目描述:

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

思路:中、右、左遍历二叉树,这样访问到的最新的一层的第一个节点一定是最右的节点。
代码:

class Solution {
    public List<Integer> ans_lst = new ArrayList<Integer>();
    public List<Integer> rightSideView(TreeNode root) {
        dfs(root, 0);
        return ans_lst;
    }
    public void dfs(TreeNode root, int depth){
        if(root==null) return;
        if(depth == ans_lst.size()){
            ans_lst.add(root.val);
        }
        depth++;
        dfs(root.right,depth);
        dfs(root.left, depth);
    }
}

24. 1457. 二叉树中的伪回文路径

题目描述:

给你一棵二叉树,每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的,当它满足:路径经过的所有节点值的排列中,存在一个回文序列。
请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。

思路1:
用哈希表记录所有的路径,最后要判断当前路径是否满足伪回文:如果只有一种数,或者奇数个数的数<=1,则是回文。但用时和内存都很拉跨。
代码1:

class Solution {
    public int ans = 0;
    public int pseudoPalindromicPaths (TreeNode root) {
        HashMap<Integer, Integer> cotmap = new HashMap<Integer, Integer>();
        dfs(root,cotmap);
        return ans;
    }
    public void dfs(TreeNode root, HashMap<Integer, Integer> cotmap){
        if(root==null){
            return;
        }
        if(cotmap.containsKey(root.val)){
            cotmap.replace(root.val, cotmap.get(root.val)+1);
        }else{
            cotmap.put(root.val, 1);
        }
        if(root.left==null && root.right==null){
            if(check_false_palindrome(cotmap)){
                ans++;
            }
            return;
        }
        dfs(root.left, new HashMap<Integer, Integer>(cotmap));
        dfs(root.right, new HashMap<Integer, Integer>(cotmap));
    }
    public boolean check_false_palindrome(HashMap<Integer, Integer> cotmap){
        if(cotmap.size()==1) return true;
        int odd_num = 0;
        for(Integer value:cotmap.values()){
            if(value%2!=0){
                odd_num++;
            }
        }
        if(odd_num<=1){
            return true;
        }
        return false;
    }
}

思路2:因为节点的值在1-9之间,所以可以使用int数组来做
代码2:

class Solution {
    public int ans = 0;
    public int pseudoPalindromicPaths (TreeNode root) {
        int[] cot_lst = new int[10];
        dfs(root,cot_lst);
        return ans;
    }
    public void dfs(TreeNode root, int[] cot_lst){
        if(root==null){
            return;
        }
        cot_lst[root.val]++;
        if(root.left==null && root.right==null){
            if(check_false_palindrome(cot_lst)){
                ans++;
            }
            cot_lst[root.val]--;
            return;
        }
        dfs(root.left, cot_lst);
        dfs(root.right, cot_lst);
        cot_lst[root.val]--;
    }
    public boolean check_false_palindrome(int[] cot_lst){
        int odd_num = 0;
        for(int i: cot_lst){
            if(i % 2 !=0){
                odd_num++;
            }
        }
        if(odd_num <= 1){
            return true;
        }
        return false;
    }
}

思路3:1.总所周知 n & (n - 1) 可以用来消除最后一个1( 确实,首先,原本n的低位上是0的地方,n-1都变成了1,然后不断向上借位,直到原本n的那一位是1,n-1对应的位置是0。所以到目前这一位位置,n&(n-1)的结果都是0,上面的位置n&(n-1)保留原来的值,所以实际上所作的操作是把n的最低位为1的1编程了0) 所以我们定义一个tmp,视其为一个二进制数,它的第0位我们不用,第1位表示1出现的次数是奇数次还是偶数次。每一次面对一个新的节点root,只需要tmp ^= (1 << root.val),就可以改变第root.val位的奇偶。
代码3:

class Solution {
    public int ans = 0;
    public int pseudoPalindromicPaths (TreeNode root) {
        dfs(root,0);
        return ans;
    }
    public void dfs(TreeNode root, int tmp){
        if(root==null) return;
        tmp ^= (1 << root.val);
        if(root.left==null && root.right==null){
            if(tmp==0|| (tmp&(tmp-1))==0){
                ans ++;
            }
            return;
        } 
        dfs(root.left, tmp);
        dfs(root.right, tmp);
    }
}

25. 695. 岛屿的最大面积

题目描述:

给定一个包含了一些 0 和 1 的非空二维数组 grid 。
一个 岛屿 是由一些相邻的 1 (代表土地) 构成的组合,这里的「相邻」要求两个 1 必须在水平或者竖直方向上相邻。你可以假设 grid 的四个边缘都被 0(代表水)包围着。
找到给定的二维数组中最大的岛屿面积。(如果没有岛屿,则返回面积为 0 。)

思路:dfs
代码:

class Solution {
    public int[] dx = {-1, 0, 1, 0};
    public int[] dy = {0, 1, 0, -1};
    public int maxAreaOfIsland(int[][] grid) {
        int ans = 0;
        for(int i=0; i<grid.length;i++){
            for(int j=0;j<grid[0].length;j++){
                if(grid[i][j]==1) ans = Math.max(dfs(grid, i, j), ans);
            }
        }
        return ans;
    }
    public int dfs(int[][] grid, int i, int j){
        int tmp = 0;
        if(grid[i][j]==1){
            grid[i][j]=0;
            tmp ++;
        }
        for(int idx=0;idx<4;idx++){
            int ni = i + dx[idx];
            int nj = j + dy[idx];
            if((0<=ni&&ni<grid.length&&0<=nj&&nj<grid[0].length)&&grid[ni][nj]==1){
                tmp += dfs(grid, ni, nj);
            }
        }
        return tmp;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值