leetcode刷题(深度优先搜索)(中等??)(TBC)

前言

我又回来啦~

26.526. 优美的排列

题目描述:

假设有从 1 到 N 的 N 个整数,如果从这 N 个数字中成功构造出一个数组,使得数组的第 i 位 (1 <= i <= N) 满足如下两个条件中的一个,我们就称这个数组为一个优美的排列。条件:
第 i 位的数字能被 i 整除
i 能被第 i 位上的数字整除
现在给定一个整数 N,请问可以构造多少个优美的排列?

思路:
定义一个n+1长的free数组,记录有哪些数字已经被使用了;cot记录了当前已经包含了多少个数字.在每次dfs开始之前,一定是选中了一个新的数字,需要修改free数组对应位置,以及cot,在每次dfs结束之后,考虑同一位置的不同可能情况,所以需要把原来的修改撤回.
代码:

class Solution {
    
    
    int ans = 0;
    int n;
    public int countArrangement(int n) {
    
    
        // i 满足, 1 - i-1, i+1, N
        this.n = n;
        int[] free = new int[n+1];
        dfs(free, 0);
        return ans;
    }
    public void dfs(int[] free, int cot){
    
    
        if(cot==n){
    
    
            ans++;
            return;
        }
        for(int i = 1;i<free.length;i++){
    
    
            if(free[i]==0&&check(cot+1, i)){
    
    
                free[i] = 1;
                cot ++;
                dfs(free, cot);
                free[i] = 0;
                cot --;
            }
        }
    }
    public boolean check(int idx, int i){
    
    
        return idx%i==0 || i%idx==0;
    }
}

官方答案:回来再看

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

题目描述:

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

思路:维护一个ArrayList num_lst,每一次"depth==num_lst.size()",代表了当前的深度最深,否则更新已存在深度的值.

class Solution {
    
    
    public List<Integer> num_lst = new ArrayList<Integer>();
    public List<Integer> largestValues(TreeNode root) {
    
    
        dfs(root,0);
        return num_lst;
    }
    public void dfs(TreeNode root,int depth){
    
    
        if(root==null) return;
        if(depth==num_lst.size()){
    
    
            num_lst.add(root.val);
        }else{
    
    
            num_lst.set(depth, Math.max(num_lst.get(depth), root.val));
        }
        dfs(root.left, depth+1);
        dfs(root.right, depth+1);
    }
}

中间的28、29、30漏了~

31. 1110. 删点成林

题目描述:

给出二叉树的根节点 root,树上每个节点都有一个不同的值。
如果节点值在 to_delete 中出现,我们就把该节点从树上删去,最后得到一个森林(一些不相交的树构成的集合)。
返回森林中的每棵树。你可以按任意顺序组织答案。

思路:
把节点加入答案需要有两个条件:

  • 节点不被删除
  • 节点没有父亲(父亲可能本来就没有(root)或者被删除了)

由于递归的思路,每一个节点既是父亲也是儿子.作为父亲,它需要:1. 判断自己要不要被删除 2. 告诉自己的左右孩子自己的状况(递归) 3. 修改自己和左右孩子的联系(根据递归的结果进行操作) 作为儿子,它需要:1. 结合自己要不要被删除和自己有没有父亲,考虑是否加入答案 2. 告诉自己的父亲自己有没有被删除(递归的结果).

代码:

class Solution {
    
    
    Set<Integer> TODELETE; 
    public List<TreeNode> delNodes(TreeNode root, int[] to_delete) {
    
    
        TODELETE = Arrays.stream(to_delete).boxed().collect(Collectors.toSet());
        List<TreeNode> ans = new ArrayList<>();
        dfs(root, ans, false);
        return ans;
    }
    public boolean dfs(TreeNode root, List<TreeNode> ans, boolean parentExists){
    
    
        //作为父亲
        if(root==null) return false;
        boolean del = TODELETE.contains(root.val);
        if(dfs(root.left, ans, !del)){
    
    
            root.left=null;
        }
        if(dfs(root.right, ans, !del)){
    
    
            root.right=null;
        }
        //作为儿子
        if(!del&&!parentExists){
    
    
            ans.add(root);
        }
        return del;
    }
}

32.547. 省份数量

题目描述:

有 n 个城市,其中一些彼此相连,另一些没有相连。如果城市 a 与城市 b 直接相连,且城市 b 与城市 c 直接相连,那么城市 a 与城市 c 间接相连。
省份 是一组直接或间接相连的城市,组内不含其他没有相连的城市。
给你一个 n x n 的矩阵 isConnected ,其中 isConnected[i][j] = 1 表示第 i 个城市和第 j 个城市直接相连,而 isConnected[i][j] = 0 表示二者不直接相连。
返回矩阵中 省份 的数量。

思路:并查集

代码:

class Solution {
    
    
    public int findCircleNum(int[][] isConnected) {
    
    
        // 并查集yyds!
        bcg BCG = new bcg(isConnected.length);
        int row = isConnected.length;
        int col = isConnected[0].length;
        for(int i = 1; i < row; i++){
    
    
            for(int j = 0; j < i; j++){
    
    
                if(isConnected[i][j]==1){
    
    
                    BCG.union(i,j);
                }
            }
        }
        return BCG.getcot();
    }
    public class bcg{
    
    
        private int cot;
        private int[] parents;
        public bcg(int size){
    
    
            cot = size;
            parents = new int[size];
            for(int i=0;i<parents.length;i++){
    
    
                parents[i] = i;
            }
        }
        public int findroot(int i){
    
    
            if(parents[i]!=i){
    
    
                parents[i] = findroot(parents[i]);
            }
            return parents[i];
        }
        public void union(int i, int j){
    
    
            int root_i = findroot(i);
            int root_j = findroot(j);
            if(root_i==root_j) return;
            parents[root_i] = root_j;
            cot --;
        }
        public int getcot(){
    
    
            return cot;
        }
    }
}

33.947. 移除最多的同行或同列石头

题目描述:

n 块石头放置在二维平面中的一些整数坐标点上。每个坐标点上最多只能有一块石头。
如果一块石头的 同行或者同列 上有其他石头存在,那么就可以移除这块石头。
给你一个长度为 n 的数组 stones ,其中 stones[i] = [xi, yi] 表示第 i 块石头的位置,返回 可以移除的石子 的最大数量。

思路1:并查集yyds,可以移除的石头的最大数量=所有的石头数-连通子图数.

代码1:

class Solution {
    
    
    public int removeStones(int[][] stones) {
    
    
        bcg BCG = new bcg(stones.length);
        for(int i=0;i<stones.length-1;i++){
    
    
            for(int j=i+1;j<stones.length;j++){
    
    
                int[] preArray = stones[i];
                int[] nowArray = stones[j];
                if(preArray[0]==nowArray[0]||preArray[1]==nowArray[1]){
    
    
                    BCG.union(i,j);
                }
            }
        }
        return stones.length - BCG.getcot();
    }
    public class bcg{
    
    
        private int cot;
        private int[] parents;
        public bcg(int size){
    
    
            cot = size;
            parents = new int[size];
            for(int i=0;i<parents.length;i++){
    
    
                parents[i] = i;
            }
        }
        public int findroot(int i){
    
    
            if(parents[i]!=i){
    
    
                parents[i] = findroot(parents[i]);
            }
            return parents[i];
        }
        public void union(int i, int j){
    
    
            int root_i = findroot(i);
            int root_j = findroot(j);
            if(root_i==root_j) return;
            parents[root_i] = root_j;
            cot --;
        }
        public int getcot(){
    
    
            return cot;
        }
    }
}

思路2:同样是并查集,但是!考虑x坐标是0到10000之间的10001个点,y坐标是10002到20002之间的10001个点,可降低时间复杂度.为了节约空间(吧),官解使用HashMap来定义parents,初始化时parents为空. 每一次union一组(x,y),会首先执行find(x)和find(y),而find函数中首先是把不存在的节点添加进去(连通图数+1),然后再进行合并
代码2:

class Solution {
    
    
    public int removeStones(int[][] stones) {
    
    
        findUnion FU = new findUnion(stones.length);
        for(int[] stone:stones){
    
    
            FU.union(stone[0], stone[1]+10001);
        }
        return stones.length - FU.get_cot();
    }
    public class findUnion{
    
    
        private int cot = 0;
        private HashMap<Integer, Integer> parents;
        public findUnion(int size){
    
    
            parents = new HashMap<Integer, Integer>(size);
        }
        public int find(int i){
    
    
            if(!parents.containsKey(i)){
    
    
                parents.put(i,i);
                cot ++;
            }
            if(parents.get(i)!=i){
    
    
                parents.put(i, find(parents.get(i)));
            }
            return parents.get(i);
        }
        public void union(int i, int j){
    
    
            int root_i = find(i);
            int root_j = find(j);
            if(root_i == root_j) return;
            parents.put(root_i, root_j);
            cot --;
        }
        public int get_cot(){
    
    
            return cot;
        }
    }
    
    
}

34. 337. 打家劫舍 III

题目描述:

在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。
计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。

思路:
定义steal和notsteal两个状态哈希表,每一个root都有两个选择,如果root偷,那么它的左孩子和右孩子一定不偷;如果root不偷,那么它的左孩子和右孩子可以偷可能不偷.

代码1:

class Solution {
    
    
    HashMap<TreeNode, Integer> steal = new HashMap<TreeNode, Integer>();
    HashMap<TreeNode, Integer> notsteal = new HashMap<TreeNode, Integer>();
    public int rob(TreeNode root) {
    
    
        dfs(root);
        return Math.max(steal.getOrDefault(root,0),notsteal.getOrDefault(root,0));
    }
    public void dfs(TreeNode root){
    
    
        if(root==null) return;
        dfs(root.left);
        dfs(root.right);
        int left_steal = steal.getOrDefault(root.left,0);
        int left_notsteal = notsteal.getOrDefault(root.left,0);
        int right_steal = steal.getOrDefault(root.right, 0);
        int right_notsteal = notsteal.getOrDefault(root.right,0);
        steal.put(root, root.val + left_notsteal + right_notsteal);
        notsteal.put(root, Math.max(left_steal, left_notsteal)+Math.max(right_steal, right_notsteal));
    }
}

代码2:用数组存储状态

class Solution {
    
    
    HashMap<TreeNode, Integer> steal = new HashMap<TreeNode, Integer>();
    HashMap<TreeNode, Integer> notsteal = new HashMap<TreeNode, Integer>();
    public int rob(TreeNode root) {
    
    
        int[] result = dfs(root);
        return Math.max(result[0],result[1]);
    }
    public int[] dfs(TreeNode root){
    
     // 0偷,1不偷
        if(root==null) return new int[2];
        int[] result_left = dfs(root.left);
        int[] result_right = dfs(root.right);
        int[] result_root = new int[2];
        result_root[0] = root.val + result_left[1] + result_right[1];
        result_root[1] = Math.max(result_left[0],result_left[1]) + Math.max(result_right[0], result_right[1]);
        return result_root;
    }
}
在这里插入代码片

35. 面试题 16.19. 水域大小

题目描述:

你有一个用于表示一片土地的整数矩阵land,该矩阵中每个点的值代表对应地点的海拔高度。若值为0则表示水域。由垂直、水平或对角连接的水域为池塘。池塘的大小是指相连接的水域的个数。编写一个方法来计算矩阵中所有池塘的大小,返回值需要从小到大排序。

思路1:dfs
代码2:

class Solution {
    
    
    int[] dx = {
    
    0,-1,-1,-1,0,1,1,1};
    int[] dy = {
    
    -1,-1,0,1,1,1,0,-1};
    int width;
    int height;
    ArrayList<Integer> ans = new ArrayList<>();
    public int[] pondSizes(int[][] land) {
    
    
        this.width = land.length;
        this.height = land[0].length;
        for(int i = 0; i < width; i++){
    
    
            for(int j = 0; j<height; j++){
    
    
                if(land[i][j]==0){
    
    
                    ans.add(dfs(i,j,land));
                }
            }
        }
        Collections.sort(ans);
        return ans.stream().mapToInt(Integer::valueOf).toArray();
    }
    public int dfs(int x, int y,int[][] land){
    
    
        land[x][y] = 1;
        int cot = 1;
        for(int idx=0;idx<dx.length;idx++){
    
    
            int nx = x+dx[idx];
            int ny = y+dy[idx];
            if(in_map(nx,ny)&&land[nx][ny]==0){
    
    
                cot += dfs(nx,ny,land);
            }
        }
        return cot;
    }
    public boolean in_map(int x,int y){
    
    
        return 0<=x&&x<width&&0<=y&&y<height;
    }
}

思路2:并查集
代码2:

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

题目描述:

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

思路:一定要先遍历右子树!
代码:

class Solution {
    
    
    public Node connect(Node root) {
    
    
        // 遇到一个节点,判断root是否有左右节点,改变左节点的next为right
        // 判断自己有没有next,如果有,判断next有没有left,如果有root.right.next = root.next.left,如果没有,判断next有没有right,如果有..
        dfs(root);
        return root;
    }
    public void dfs(Node root){
    
    
        if(root==null) return;
        if(root.left!=null && root.right!=null){
    
    
            root.left.next = root.right;
        }
        Node next_root = root.next;
        while(next_root!=null&&next_root.left==null&&next_root.right==null){
    
    
            next_root = next_root.next;
        }
        if(next_root!=null){
    
    
            if(root.right!=null){
    
    
                if(next_root.left!=null){
    
    
                    root.right.next = next_root.left;
                }else{
    
    
                    root.right.next = next_root.right;
                }
            }else{
    
    
                if(root.left!=null){
    
    
                    if(next_root.left!=null){
    
    
                        root.left.next = next_root.left;
                    }else{
    
    
                        root.left.next = next_root.right;
                    }
                }
            }
        }
        dfs(root.right);
        dfs(root.left);
    }
}

猜你喜欢

转载自blog.csdn.net/jokerxsy/article/details/114266347
今日推荐