LeetCode-20200831-20200906

9 篇文章 0 订阅

钥匙和房间

  • 题目。有 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。
    示例 1:
    输入: [[1],[2],[3],[]]
    输出: true
    解释:
    我们从 0 号房间开始,拿到钥匙 1。
    之后我们去 1 号房间,拿到钥匙 2。
    然后我们去 2 号房间,拿到钥匙 3。
    最后我们去了 3 号房间。
    由于我们能够进入每个房间,我们返回 true。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/keys-and-rooms

  • 思路。有向图的遍历问题。可以将每个房间抽象成图中顶点,将每个房间能到达的房间抽象成边。如果从顶点0出发可以到达所有的顶点,则返回true,否则返回false。
  • 代码。
class Solution {
    public boolean canVisitAllRooms(List<List<Integer>> rooms) {
        // 图的遍历,当遍历完所有房间返回true,如果中途变量不下去了返回false
        int r = rooms.size();
        boolean[] visited = new boolean[r];
        Queue<Integer> queue = new LinkedList<>();
        queue.offer(0);
        while (!queue.isEmpty()) {
            int cur = queue.poll();
            if (visited[cur]) {
                continue;
            }
            visited[cur] = true;
            List<Integer> edges = rooms.get(cur);
            if (edges != null && edges.size() > 0) {
                for (int neighbor : edges) {
                    queue.offer(neighbor);
                }
            }
        }

        for (int i = 0; i < visited.length; i++) {
            if (!visited[i]) {
                return false;
            }
        }

        return true;
    }
}

预测赢家

  • 问题。给定一个表示分数的非负整数数组。 玩家 1 从数组任意一端拿取一个分数,随后玩家 2 继续从剩余数组任意一端拿取分数,然后玩家 1 拿,…… 。每次一个玩家只能拿取一个分数,分数被拿取之后不再可取。直到没有剩余分数可取时游戏结束。最终获得分数总和最多的玩家获胜。
    给定一个表示分数的数组,预测玩家1是否会成为赢家。你可以假设每个玩家的玩法都会使他的分数最大化。
    示例 1:
    输入:[1, 5, 2]
    输出:False
    解释:一开始,玩家1可以从1和2中进行选择。
    如果他选择 2(或者 1 ),那么玩家 2 可以从 1(或者 2 )和 5 中进行选择。如果玩家 2 选择了 5 ,那么玩家 1 则只剩下 1(或者 2 )可选。
    所以,玩家 1 的最终分数为 1 + 2 = 3,而玩家 2 为 5 。
    因此,玩家 1 永远不会成为赢家,返回 False 。
    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/predict-the-winner
  • 思路1。先给个暴力递归的解法。假设我们有两个方法,一个是int pickFirst(int[] nums, int i, int j),代表从nums i到j先拿可以得到的最大分数;另一个方法是int pickLast(int[] nums, int i, int j),代表从nums i到j后拿可以得到的最大分数,所以返回pickFirst(nums, 0, nums.length - 1) >= pickLast(nums, 0, nums.length - 1) 即可。
  • 代码1。
	public boolean PredictTheWinner(int[] nums) {
        return pickFirst(nums, 0, nums.length - 1) >= pickLast(nums, 0, nums.length - 1);
    }

    private int pickFirst(int[] nums, int i, int j) {
        // 从i到j先选,i==j表示只剩一个了,则先选的人可以拿个这个数
        if (i == j) {
            return nums[i];
        }
        
		// 选i然后在i+1到j后选,或者选j然后在i到j-1后选,取二者较大的值 
        return Math.max(nums[i] + pickLast(nums, i + 1, j), nums[j] + pickLast(nums, i, j - 1));
    }


    private int pickLast(int[] nums, int i, int j) {
        // 从i到j后选,i==j表示只剩一个了,则后选的人拿不到分数
        if (i == j) {
            return 0;
        }

		// 后选的人只能得到从i+1到j先选和从i到j-1先选较小的分数
        return Math.min(pickFirst(nums, i + 1, j), pickFirst(nums, i, j - 1));
    }
  • 思路2。上面的暴力递归的解法存在重复计算的问题,可以改为动态规划。我们使用dp[i][j]表示可选nums[i…j]时,当前操作的选手(注意不一定是第一位选手)与另一位选手的最大分差。这里需要满足i<j并且dp[i][i]=nums[i]。我们在求dp[i][j]时, 如果选nums[i],则分差是nums[i]-dp[i+1][j];如果选nums[j],则分差是nums[j] - dp[i+1][j],取二者的最大值即可。
  • 代码2。
   public boolean PredictTheWinner(int[] nums) {
     if (nums == null || nums.length == 0) {
         return false;
     }

     // dp[i][j]表示可选nums[i...j]时,当前操作的选手(注意不一定是第一位选手)与另一位选手的最大分差
     int[][] dp = new int[nums.length][nums.length];
     // 只能在num[i...i]选,则最大分差就是nums[i],即dp[i][i]=nums[i]
     for (int i = 0; i < nums.length; i++) {
         dp[i][i] = nums[i];
     }

     // dp[i][j]=Math.max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1])
     for (int i = nums.length - 2; i >= 0; i--) {
         for (int j = i + 1; j < nums.length; j++) {
             dp[i][j] = Math.max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);
         }
     }

     return dp[0][nums.length - 1] >= 0;
 }

n 皇后问题

  • 问题。n皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
    每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
    输入:4
    输出:[
    [".Q…", // 解法 1
    “…Q”,
    “Q…”,
    “…Q.”],
    ["…Q.", // 解法 2
    “Q…”,
    “…Q”,
    “.Q…”]
    ]
    解释: 4 皇后问题存在两个不同的解法。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/n-queens

  • 思路。经典的回溯问题,也就是说我们通过dfs进行尝试,如果第i行的第j列可以放置一个皇后,那么我们尝试在第i+1行基于已有的结果继续尝试。第i行的第j列尝试完成后,我们尝试在第i行第j+1列放置一个皇后,然后重复之前的过程。
  • 代码。
class Solution {

    public List<List<String>> solveNQueens(int n) {
        if (n <= 0) {
            return new ArrayList<>();
        }

        List<List<String>> res = new ArrayList<>();
        f(0, new ArrayList<>(), n, res);
        return res;
    }

    private void f(int i, List<String> list, int n, List<List<String>> res) {
        if (i == n && list.size() == n) {
            res.add(new ArrayList<>(list));
            return;
        }

        for (int j = 0; j < n; j++) {
            if (canPlace(list, i, j, n)) {
                String cur = getCurrentPlacement(j, n);
                list.add(cur);
                f(i + 1, list, n, res);
                list.remove(list.size() - 1);
            }
        }
    }

    /**
     * 已经放置的皇后保存在list中,看i j位置是否可以放置皇后
     * */
    private boolean canPlace(List<String> list, int i, int j, int n) {
        // i j的正上方是否有皇后
        int p = i - 1;
        int q = 0;
        while (p >= 0) {
            if (list.get(p).charAt(j) == 'Q') {
                return false;
            }
            p--;
        }

        // i j的左上方是否有皇后
        p = i - 1;
        q = j - 1;
        while (p >= 0 && q >= 0) {
            if (list.get(p).charAt(q) == 'Q') {
                return false;
            }
            p--;
            q--;
        }

        // i j的右上方是否有皇后
        p = i - 1;
        q = j + 1;
        while (p >= 0 && p < n && q >= 0 && q < n) {
            if (list.get(p).charAt(q) == 'Q') {
                return false;
            }
            p--;
            q++;
        }

        return true;
    }

    /**
     * 第j列是皇后
     * */
    private String getCurrentPlacement(int j, int n) {
        StringBuilder res = new StringBuilder();
        for (int i = 0; i < n; i++) {
            String cur = i == j ? "Q" : ".";
            res.append(cur);
        }

        return res.toString();
    }

    /**
     * queue记录已经摆放的皇后的位置,i表示当前要放第i行的皇后(此时第0到i-1行已经放完皇后了)
     * */
    private void dfs(char[][] queen, int i, int n, List<List<String>> res) {
        if (i == n) {
            res.add(fill(queen));
            return;
        }

        for (int j = 0; j < n; j++) {
            if (queen[i][j] == 'Q' || canNotPlace(queen, i, j, n)) {
                continue;
            }

            queen[i][j] = 'Q';
            dfs(queen, i + 1, n, res);
            queen[i][j] = '.';
        }
    }

    private List<String> fill(char[][] queen) {
        List<String> list = new ArrayList<>();
        for (char[] chars : queen) {
            list.add(String.valueOf(chars));
        }
        return list;
    }

    private boolean canNotPlace(char[][] queen, int i, int j, int n) {
        // 行
        for (int p = 0; p < n; p++) {
            if (queen[i][p] == 'Q') {
                return true;
            }
        }

        // 列
        for (int p = 0; p < i; p++) {
            if (queen[p][j] == 'Q') {
                return true;
            }
        }

        // 左上
        int up = i - 1;
        int left = j - 1;
        while (up >= 0 && left >= 0) {
            if (queen[up][left] == 'Q') {
                return true;
            }
            up -= 1;
            left -= 1;
        }

        // 右上
        up = i - 1;
        int right = j + 1;
        while (up >= 0 && right < n) {
            if (queen[up][right] == 'Q') {
                return true;
            }
            up -= 1;
            right += 1;
        }

        return false;
    }
}

二叉树的所有路径

  • 题目。给定一个二叉树,返回所有从根节点到叶子节点的路径。
    说明: 叶子节点是指没有子节点的节点。
    来源:力扣(LeetCode)
    链接:https://leetcode-cn.com/problems/binary-tree-paths
  • 思路。二叉树的遍历。走到叶子节点则将获取的答案加入到结果集。
  • 代码。
/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode(int x) { val = x; }
 * }
 */
class Solution {

    TreeNode r;

    public List<String> binaryTreePaths(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }
        r = root;
        List<String> res = new ArrayList<>();
        f(root, "", res);
        return res;
    }

    private void f(TreeNode cur, String s, List<String> res) {
        if (cur == null) {
            return;
        }

        if (cur.left == null && cur.right == null) {
            if (cur == r) {
                res.add(cur.val + "");
            } else {
                res.add(s + "->" + cur.val);
            }
            return;
        }

        if (cur == r) {
            f(cur.left, cur.val + "", res);
            f(cur.right, cur.val + "", res);
        } else {
            f(cur.left, s + "->" + cur.val, res);
            f(cur.right, s + "->" + cur.val, res);
        }
        
    }
    
}

第k个排列

  • 题目。给出集合 [1,2,3,…,n],其所有元素共有 n! 种排列。
    按大小顺序列出所有排列情况,并一一标记,当 n = 3 时, 所有排列如下:
    “123”
    “132”
    “213”
    “231”
    “312”
    “321”
    给定 n 和 k,返回第 k 个排列。
    说明:

给定 n 的范围是 [1, 9]。
给定 k 的范围是[1, n!]。
示例 1:
输入: n = 3, k = 3
输出: “213”
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/permutation-sequence

  • 思路。直接写permutation会超时。需要加上剪枝。
  • 代码。
class Solution {

    private int n;
    private int k;
    private boolean[] used;
    private int[] factorial;

    public String getPermutation(int n, int k) {
        this.n = n;
        this.k = k;
        used = new boolean[n + 1];

        factorial = new int[n + 1];
        factorial[0] = 1;
        for (int i = 1; i <= n; i++) {
            factorial[i] = factorial[i - 1] * i;
        }

        StringBuilder path = new StringBuilder();
        f(0, path);
        return path.toString();
    }

    private void f(int index, StringBuilder path) {
        if (index == n) {
            return;
        }

        // 计算还未确定的数字的全排列的个数,第 1 次进入的时候是 n - 1
        int count = factorial[n - 1 - index];
        for (int i = 1; i <=n ; i++) {
            if (used[i]) {
                continue;
            }

            if (count < k) {
                k -= count;
                continue;
            }

            path.append(i);
            used[i] = true;
            f(index + 1, path);
            return;
        }
    }

	// 超时的代码
    // public String getPermutation(int n, int k) {
    //     List<String> res = new ArrayList<>();
    //     boolean[] used = new boolean[n];
    //     f(n, "", used, res);
    //     return res.get(k - 1);
    // }

    // private void f(int n, String cur, boolean[] used, List<String> res) {
    //     if (cur.length() == n) {
    //         res.add(cur);
    //         return;
    //     }

    //     for (int i = 0; i < n; i++) {
    //         if (used[i]) {
    //             continue;
    //         }

    //         used[i] = true;
    //         f(n, cur + (i + 1), used, res);
    //         used[i] = false;
    //     }
    // }
}


二叉树的层次遍历 II

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

  • 思路。二叉树的层次遍历。这里使用last表示当前层的最后一个节点,使用nextLast表示下一层的最后一个节点。

  • 代码。

public List<List<Integer>> levelOrderBottom(TreeNode root) {
        if (root == null) {
            return new ArrayList<>();
        }

        LinkedList<List<Integer>> res = new LinkedList<>();
        Queue<TreeNode> queue = new LinkedList<>();
        queue.offer(root);
        TreeNode last = root;
        TreeNode nextLast = null;
        List<Integer> list = new ArrayList<>();
        while (!queue.isEmpty()) {
            TreeNode cur = queue.poll();
            list.add(cur.val);
            
            if (cur.left != null) {
                queue.offer(cur.left);
                nextLast = cur.left;
            }
            
            if (cur.right != null) {
                queue.offer(cur.right);
                nextLast = cur.right;
            }

            if (cur == last) {
                res.addFirst(new ArrayList<>(list));
                list = new ArrayList<>();
                last = nextLast;
            }
        }
        
        return res;
    }
LeetCode-Editor是一种在线编码工具,它提供了一个用户友好的界面编写和运行代码。在使用LeetCode-Editor,有候会出现乱码的问题。 乱码的原因可能是由于编码格式不兼容或者编码错误导致的。在这种情况下,我们可以尝试以下几种解决方法: 1. 检查文件编码格式:首先,我们可以检查所编辑的文件的编码格式。通常来说,常用的编码格式有UTF-8和ASCII等。我们可以将编码格式更改为正确的格式。在LeetCode-Editor中,可以通过界面设置或编辑器设置来更改编码格式。 2. 使用正确的字符集:如果乱码是由于使用了不同的字符集导致的,我们可以尝试更改使用正确的字符集。常见的字符集如Unicode或者UTF-8等。在LeetCode-Editor中,可以在编辑器中选择正确的字符集。 3. 使用合适的编辑器:有候,乱码问题可能与LeetCode-Editor自身相关。我们可以尝试使用其他编码工具,如Text Editor、Sublime Text或者IDE,看是否能够解决乱码问题。 4. 查找特殊字符:如果乱码问题只出现在某些特殊字符上,我们可以尝试找到并替换这些字符。通过仔细检查代码,我们可以找到导致乱码的特定字符,并进行修正或替换。 总之,解决LeetCode-Editor乱码问题的方法有很多。根据具体情况,我们可以尝试更改文件编码格式、使用正确的字符集、更换编辑器或者查找并替换特殊字符等方法来解决这个问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值