美文网首页
LeetCode 311-340

LeetCode 311-340

作者: 1nvad3r | 来源:发表于2020-11-26 21:20 被阅读0次

    312. 戳气球

    把nums数组扩充一下,首尾加一个数字1。
    dp[i][j]代表开区间(i,j)所能获得硬币的最大数量。
    边界:
    (i,j)区间长度小于等于2时,能获得的硬币数为0。
    转移方程:
    遍历开区间内的所有值,记为k,k代表这个开区间(i,j)最后一个戳破的气球。
    那么开区间(i,j)的收益就为dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j]。
    dp[i][j]等于所有的k取收益最大的一个。
    区间长度从3开始遍历,直到区间长度达到数组长度。这样才能保证dp数组更新正确。
    最后答案就为dp[0][nums.length-1]

    class Solution {
        public int maxCoins(int[] nums) {
            int[] newNums = new int[nums.length + 2];
            newNums[0] = newNums[nums.length + 1] = 1;
            System.arraycopy(nums, 0, newNums, 1, nums.length);
            nums = newNums;
            int[][] dp = new int[nums.length][nums.length];
            for (int L = 3; L <= nums.length; L++) {
                for (int i = 0; i <= nums.length - L; i++) {
                    int res = 0;
                    int j = i + L - 1;
                    for (int k = i + 1; k < j; k++) {
                        int left = dp[i][k];
                        int right = dp[k][j];
                        res = Math.max(res, left + nums[i] * nums[k] * nums[j] + right);
                    }
                    dp[i][j] = res;
                }
            }
            return dp[0][nums.length - 1];
        }
    }
    

    313. 超级丑数

    多指针,思想和第264. 丑数 II一样。
    pos[j]代表应该用丑数数组uglies中的第几个数去乘以质数数组primes中的第j个数。 初始pos[j]都等于0,每一轮遍历时,都先选出下一个最小的丑数,为了避免重复,需要把所有的uglies[pos[j]] * primes[j] 等于这个丑数的pos[j]进行加1。

    class Solution {
        public int nthSuperUglyNumber(int n, int[] primes) {
            int k = primes.length;
            int[] uglies = new int[n], pos = new int[k];
            uglies[0] = 1;
            for (int i = 1; i < n; i++) {
                int min = Integer.MAX_VALUE, index = 0;
                for (int j = 0; j < k; j++) {
                    if (uglies[pos[j]] * primes[j] < min) {
                        min = uglies[pos[j]] * primes[j];
                        index = j;
                    }
                }
                for (int j = 0; j < k; j++) {
                    if (min == uglies[pos[j]] * primes[j]) {
                        pos[j]++;
                    }
                }
                uglies[i] = min;
            }
            return uglies[n - 1];
        }
    }
    

    315. 计算右侧小于当前元素的个数

    暴力法,超时:

    class Solution {
        public List<Integer> countSmaller(int[] nums) {
            List<Integer> res = new ArrayList<>();
            for (int i = 0; i < nums.length; i++) {
                int count = 0;
                for (int j = i + 1; j < nums.length; j++) {
                    if (nums[j] < nums[i]) {
                        count++;
                    }
                }
                res.add(count);
            }
            return res;
        }
    }
    

    树状数组:

    316. 去除重复字母

    先使用一个Map记录每一个字符最后出现的位置,使用一个Set记录某个字符是否在栈中。
    遍历字符串,如果栈中没有这个字符,将它进栈,但是进栈之前还需要判断一下:
    如果栈顶元素比它大,且栈顶元素之后还会出现,那么将栈顶元素出栈,Set中也要移除栈顶元素。
    最后把所有元素出栈就是结果的倒序。

    class Solution {
        public String removeDuplicateLetters(String s) {
            Deque<Character> stack = new ArrayDeque<>();
            Set<Character> isVisit = new HashSet<>();
            Map<Character, Integer> lastPos = new HashMap<>();
            for (int i = 0; i < s.length(); i++) {
                lastPos.put(s.charAt(i), i);
            }
            for (int i = 0; i < s.length(); i++) {
                char c = s.charAt(i);
                if (!isVisit.contains(c)) {
                    while (!stack.isEmpty() && c < stack.peek() && lastPos.get(stack.peek()) > i) {
                        isVisit.remove(stack.pop());
                    }
                    isVisit.add(c);
                    stack.push(c);
                }
            }
            StringBuilder res = new StringBuilder();
            while (!stack.isEmpty()) {
                res.append(stack.pop());
            }
            res.reverse();
            return res.toString();
        }
    }
    

    318. 最大单词长度乘积

    class Solution {
        public int maxProduct(String[] words) {
            Arrays.sort(words, (o1, o2) -> o2.length() - o1.length());
            int res = 0;
            for (int i = 0; i < words.length; i++) {
                Set<Character> set = new HashSet<>();
                for (char c : words[i].toCharArray()) {
                    set.add(c);
                }
                for (int j = i + 1; j < words.length; j++) {
                    boolean flag = true;
                    for (char c : words[j].toCharArray()) {
                        if (set.contains(c)) {
                            flag = false;
                            break;
                        }
                    }
                    if (flag == true) {
                        res = Math.max(res, words[i].length() * words[j].length());
                    }
                }
            }
            return res;
        }
    }
    

    319. 灯泡开关

    数学题,找规律,只有是完全平方的数最后是亮着的,1到n中完全平方的个数就等于 sqrt(n)向下取整。

    class Solution {
        public int bulbSwitch(int n) {
            return (int)Math.sqrt(n * 1.0);
        }
    }
    

    322. 零钱兑换

    回溯,超时:

    class Solution {
        int res = Integer.MAX_VALUE;
        List<Integer> temp = new ArrayList<>();
    
        private void dfs(int begin, int[] coins, int amount) {
            if (begin > coins.length || amount < 0) {
                return;
            }
            if (amount == 0) {
                res = Math.min(res, temp.size());
                return;
            }
            for (int i = begin; i < coins.length; i++) {
                temp.add(coins[i]);
                dfs(i, coins, amount - coins[i]);
                temp.remove(temp.size() - 1);
            }
        }
    
        public int coinChange(int[] coins, int amount) {
            Arrays.sort(coins);
            int i = 0, j = coins.length - 1;
            while (i < j) {
                int temp = coins[i];
                coins[i] = coins[j];
                coins[j] = temp;
                i++;
                j--;
            }
            dfs(0, coins, amount);
            return res == Integer.MAX_VALUE ? -1 : res;
        }
    }
    

    动态规划:
    dp[i]代表凑出金额为i所需最少的硬币数。 初始都赋为amount+1 ,如果最终dp[i]的值是amount+1,说明无法凑出。
    边界:
    dp[0] = 0
    转移方程:
    从所有硬币中选择一个硬币j,只要这个硬币j的金额小于等于i,dp[i]就等于dp[i-coins[j]] + 1。 遍历所有的硬币,dp[i]等于其中最小的那个。

    最终答案就是dp[amount]。

    class Solution {
        public int coinChange(int[] coins, int amount) {
            int[] dp = new int[amount + 1];
            Arrays.fill(dp, amount + 1);
            dp[0] = 0;
            for (int i = 1; i <= amount; i++) {
                for (int j = 0; j < coins.length; j++) {
                    if (coins[j] <= i) {
                        dp[i] = Math.min(dp[i], dp[i - coins[j]] + 1);
                    }
                }
            }
            return dp[amount] > amount ? -1 : dp[amount];
        }
    }
    

    324. 摆动排序 II

    先排序,然后左半部分和右半部分交叉填入结果中,为了保证答案正确,左边每次要先选最大的,右边每次也要先选最大的。

    class Solution {
        public void wiggleSort(int[] nums) {
            Arrays.sort(nums);
            int i = (int) Math.ceil(nums.length / 2.0) - 1, j = nums.length - 1;
            int[] res = new int[nums.length];
            int pos = 0;
            while (pos != nums.length) {
                res[pos++] = nums[i--];
                if (pos == nums.length) {
                    break;
                }
                res[pos++] = nums[j--];
            }
            for (int k = 0; k < nums.length; k++) {
                nums[k] = res[k];
            }
        }
    }
    

    326. 3的幂

    class Solution {
        public boolean isPowerOfThree(int n) {
            if (n <= 0) {
                return false;
            }
            while (n % 3 == 0) {
                n /= 3;
            }
            return n == 1;
        }
    }
    

    327. 区间和的个数

    前缀和暴力法:

    class Solution {
        public int countRangeSum(int[] nums, int lower, int upper) {
            long[] pre = new long[nums.length];
            int res = 0;
            for (int i = 0; i < nums.length; i++) {
                pre[i] = (i - 1 >= 0 ? pre[i - 1] : 0) + nums[i];
            }
            for (int i = 0; i < nums.length; i++) {
                for (int j = i; j < nums.length; j++) {
                    if (pre[j] - (i - 1 >= 0 ? pre[i - 1] : 0) >= lower &&
                            pre[j] - (i - 1 >= 0 ? pre[i - 1] : 0) <= upper) {
                        res++;
                    }
                }
            }
            return res;
        }
    }
    

    树状数组:

    328. 奇偶链表

    class Solution {
        public ListNode oddEvenList(ListNode head) {
            if (head == null) {
                return null;
            }
            ListNode evenHead = head.next;
            ListNode curOdd = head, curEven = evenHead;
            while (curEven != null && curEven.next != null) {
                curOdd.next = curEven.next;
                curEven.next = curEven.next.next;
                curOdd = curOdd.next;
                curEven = curEven.next;
            }
            curOdd.next = evenHead;
            return head;
        }
    }
    

    329. 矩阵中的最长递增路径

    dfs 超时:

    class Solution {
        int res = 1;
        List<Integer> temp = new ArrayList<>();
        boolean[][] isVisit;
        int[] X = {1, -1, 0, 0}, Y = {0, 0, 1, -1};
    
        private void dfs(int i, int j, int[][] matrix) {
            if (i < 0 || i >= matrix.length || j < 0 || j >= matrix[0].length || isVisit[i][j] == true) {
                return;
            }
            if (temp.size() > 0 && matrix[i][j] <= temp.get(temp.size() - 1)) {
                res = Math.max(res, temp.size());
                return;
            }
            temp.add(matrix[i][j]);
            res = Math.max(res, temp.size());
            isVisit[i][j] = true;
            for (int k = 0; k < 4; k++) {
                int newI = i + X[k], newJ = j + Y[k];
                dfs(newI, newJ, matrix);
            }
            temp.remove(temp.size() - 1);
            isVisit[i][j] = false;
        }
    
        public int longestIncreasingPath(int[][] matrix) {
            if (matrix.length == 0) {
                return 0;
            }
            isVisit = new boolean[matrix.length][matrix[0].length];
            for (int i = 0; i < matrix.length; i++) {
                for (int j = 0; j < matrix[0].length; j++) {
                    dfs(i, j, matrix);
                }
            }
            return res;
        }
    }
    

    记忆化dfs:

    class Solution {
        int[][] dp;
        int[] X = {0, 0, 1, -1}, Y = {1, -1, 0, 0};
    
        private int dfs(int i, int j, int[][] matrix) {
            if (dp[i][j] != 0) {
                return dp[i][j];
            }
            dp[i][j]++;
            for (int k = 0; k < 4; k++) {
                int newI = i + X[k], newJ = j + Y[k];
                if (newI >= 0 && newI < matrix.length && newJ >= 0 && newJ < matrix[0].length && matrix[newI][newJ] > matrix[i][j]) {
                    dp[i][j] = Math.max(dp[i][j], dfs(newI, newJ, matrix) + 1);
                }
            }
            return dp[i][j];
        }
    
        public int longestIncreasingPath(int[][] matrix) {
            if (matrix.length == 0) {
                return 0;
            }
            int row = matrix.length, col = matrix[0].length;
            dp = new int[row][col];
            int res = 0;
            for (int i = 0; i < row; i++) {
                for (int j = 0; j < col; j++) {
                    res = Math.max(res, dfs(i, j, matrix));
                }
            }
            return res;
        }
    }
    

    330. 按要求补齐数组

    public class Solution {
        public int minPatches(int[] nums, int n) {
            int res = 0, i = 0;
            long miss = 1;
            while (miss <= n) {
                if (i < nums.length && nums[i] <= miss) {
                    miss += nums[i++];
                } else {
                    miss += miss;
                    res++;
                }
            }
            return res;
        }
    }
    

    331. 验证二叉树的前序序列化

    class Solution {
        public boolean isValidSerialization(String preorder) {
            int slots = 1;
            String[] split = preorder.split(",");
            for (String s : split) {
                slots--;
                if (slots < 0) {
                    return false;
                }
                if (!"#".equals(s)) {
                    slots += 2;
                }
            }
            return slots == 0;
        }
    }
    

    332. 重新安排行程

    通过图中所有边恰好一次且行遍所有顶点的通路称为欧拉通路。
    通过图中所有边恰好一次且行遍所有顶点的回路称为欧拉回路。
    具有欧拉回路的无向图称为欧拉图。
    具有欧拉通路但不具有欧拉回路的无向图称为半欧拉图。

    本题是欧拉图或半欧拉图。使用优先队列贪心的每次往字典序更小的结点进行dfs,直到没有新的到达为止,就把这个结点加入到结果中。最后的答案是将res反转得到。

    class Solution {
        Map<String, PriorityQueue<String>> map = new HashMap<String, PriorityQueue<String>>();
        List<String> res = new LinkedList<String>();
    
        public List<String> findItinerary(List<List<String>> tickets) {
            for (List<String> ticket : tickets) {
                String from = ticket.get(0), to = ticket.get(1);
                if (!map.containsKey(from)) {
                    map.put(from, new PriorityQueue<String>());
                }
                map.get(from).offer(to);
            }
            dfs("JFK");
            Collections.reverse(res);
            return res;
        }
    
        public void dfs(String curr) {
            while (map.containsKey(curr) && map.get(curr).size() > 0) {
                String tmp = map.get(curr).poll();
                dfs(tmp);
            }
            res.add(curr);
        }
    }
    

    334. 递增的三元子序列

    a 始终记录最小元素,b 为某个子序列里第二大的数。
    接下来不断更新 a,同时保持 b 尽可能的小。
    如果下一个元素比 b 大,说明找到了三元组。

    class Solution {
        public boolean increasingTriplet(int[] nums) {
            int a = Integer.MAX_VALUE, b = Integer.MAX_VALUE;
            for (int i : nums) {
                if (i <= a) {
                    a = i;
                } else if (i <= b) {
                    b = i;
                } else {
                    return true;
                }
            }
            return false;
        }
    }
    

    336. 回文对

    暴力法,超时:

    class Solution {
        private boolean isPar(String str) {
            if ("".equals(str)) {
                return true;
            }
            int i = 0, j = str.length() - 1;
            while (i < j) {
                if (str.charAt(i++) != str.charAt(j--)) {
                    return false;
                }
            }
            return true;
        }
    
        public List<List<Integer>> palindromePairs(String[] words) {
            List<List<Integer>> res = new ArrayList<>();
            for (int i = 0; i < words.length; i++) {
                for (int j = i + 1; j < words.length; j++) {
                    if (isPar(words[i] + words[j])) {
                        res.add(Arrays.asList(i, j));
                    }
                    if (isPar(words[j] + words[i])) {
                        res.add(Arrays.asList(j, i));
                    }
                }
            }
            return res;
        }
    }
    

    337. 打家劫舍 III

    树形dp:
    dp[0]代表偷当前结点的最大金额,dp[1]代表不偷当前结点的最大金额。
    采取后序遍历,这样当到达根结点时,就已经知道左右子结点的信息了。

    如果不偷当前结点,dp[0]等于左子树的最大金额加上右子树的最大金额。
    如果偷当前结点,dp[1]等于当前结点金额加上左子树不偷的金额加上右子树不偷的金额。

    class Solution {
        private int[] dfs(TreeNode root) {
            if (root == null) {
                return new int[]{0, 0};
            }
            int[] left = dfs(root.left);
            int[] right = dfs(root.right);
            int[] dp = new int[2];
            dp[0] = Math.max(left[0], left[1]) + Math.max(right[0], right[1]);
            dp[1] = left[0] + right[0] + root.val;
            return dp;
        }
    
        public int rob(TreeNode root) {
            int[] res = dfs(root);
            return Math.max(res[0], res[1]);
        }
    }
    

    338. 比特位计数

    n & (n-1)能把n中最低位的1变成0,其它位不变。
    这样就很快计算出n中1的个数。

    class Solution {
        public int[] countBits(int num) {
            int[] res = new int[num + 1];
            for (int i = 0; i <= num; i++) {
                res[i] = count(i);
            }
            return res;
        }
    
        private int count(int n) {
            int sum = 0;
            while (n != 0) {
                sum++;
                n &= n - 1;
            }
            return sum;
        }
    
    }
    

    相关文章

      网友评论

          本文标题:LeetCode 311-340

          本文链接:https://www.haomeiwen.com/subject/czrniktx.html