diff --git "a/src/main/java/algorithm_practice/Coding_Interview_Guide_2ndEdition/Chapter_04_\351\200\222\345\275\222\345\222\214\345\212\250\346\200\201\350\247\204\345\210\222/P04_\346\215\242\351\222\261\347\232\204\346\234\200\345\260\221\350\264\247\345\270\201\346\225\260.java" "b/src/main/java/algorithm_practice/Coding_Interview_Guide_2ndEdition/Chapter_04_\351\200\222\345\275\222\345\222\214\345\212\250\346\200\201\350\247\204\345\210\222/P04_\346\215\242\351\222\261\347\232\204\346\234\200\345\260\221\350\264\247\345\270\201\346\225\260.java" deleted file mode 100644 index af52f29a..00000000 --- "a/src/main/java/algorithm_practice/Coding_Interview_Guide_2ndEdition/Chapter_04_\351\200\222\345\275\222\345\222\214\345\212\250\346\200\201\350\247\204\345\210\222/P04_\346\215\242\351\222\261\347\232\204\346\234\200\345\260\221\350\264\247\345\270\201\346\225\260.java" +++ /dev/null @@ -1,17 +0,0 @@ -package algorithm_practice.Coding_Interview_Guide_2ndEdition.Chapter_04_递归和动态规划; - -import junit.framework.TestCase; -import org.junit.Test; - -/* - -Created by nibnait on 2020-03-15 - */ -public class P04_换钱的最少货币数 extends TestCase { - - @Test - public void testCase() { - - } - -} diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/M062_\344\270\215\345\220\214\350\267\257\345\276\204.java" "b/src/main/java/algorithm_practice/LeetCode/code000/M062_\344\270\215\345\220\214\350\267\257\345\276\204.java" new file mode 100644 index 00000000..109adc31 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/M062_\344\270\215\345\220\214\350\267\257\345\276\204.java" @@ -0,0 +1,84 @@ +package algorithm_practice.LeetCode.code000; + +import org.junit.Assert; +import org.junit.Test; + +/* +一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + +机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + +问总共有多少条不同的路径? + + + +例如,上图是一个7 x 3 的网格。有多少可能的路径? + +  + +示例 1: + +输入: m = 3, n = 2 +输出: 3 +解释: +从左上角开始,总共有 3 条路径可以到达右下角。 +1. 向右 -> 向右 -> 向下 +2. 向右 -> 向下 -> 向右 +3. 向下 -> 向右 -> 向右 +示例 2: + +输入: m = 7, n = 3 +输出: 28 +  + +提示: + +1 <= m, n <= 100 +题目数据保证答案小于等于 2 * 10 ^ 9 + + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/unique-paths +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class M062_不同路径 { + @Test + public void testCase() { + int m = 3; + int n = 2; + int excepted = 3; + Assert.assertEquals(excepted, uniquePaths(m, n)); + + m = 7; + n = 3; + excepted = 28; + Assert.assertEquals(excepted, uniquePaths(m, n)); + + m = 3; + n = 7; + excepted = 28; + Assert.assertEquals(excepted, uniquePaths(m, n)); + + } + + public int uniquePaths(int m, int n) { + + int[][] dp = new int[m][n]; + + for (int i = 0; i < m; i++) { + dp[i][0] = 1; + } + + for (int i = 0; i < n; i++) { + dp[0][i] = 1; + } + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i][j] = dp[i-1][j] + dp[i][j-1]; + } + } + + return dp[m-1][n-1]; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code000/M063_\344\270\215\345\220\214\350\267\257\345\276\2042.java" "b/src/main/java/algorithm_practice/LeetCode/code000/M063_\344\270\215\345\220\214\350\267\257\345\276\2042.java" new file mode 100644 index 00000000..4a693364 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code000/M063_\344\270\215\345\220\214\350\267\257\345\276\2042.java" @@ -0,0 +1,115 @@ +package algorithm_practice.LeetCode.code000; + +import org.junit.Assert; +import org.junit.Test; + +/* +一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为“Start” )。 + +机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为“Finish”)。 + +现在考虑网格中有障碍物。那么从左上角到右下角将会有多少条不同的路径? + + + +网格中的障碍物和空位置分别用 1 和 0 来表示。 + +说明:m 和 n 的值均不超过 100。 + +示例 1: + +输入: +[ +  [0,0,0], +  [0,1,0], +  [0,0,0] +] +输出: 2 +解释: +3x3 网格的正中间有一个障碍物。 +从左上角到右下角一共有 2 条不同的路径: +1. 向右 -> 向右 -> 向下 -> 向下 +2. 向下 -> 向下 -> 向右 -> 向右 + + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/unique-paths-ii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class M063_不同路径2 { + @Test + public void testCase() { + int[][] obstacleGrid = new int[][]{ + {0,0,0}, + {0,1,0}, + {0,0,0}}; + int expected = 2; + Assert.assertEquals(expected, uniquePathsWithObstacles(obstacleGrid)); + + obstacleGrid = new int[][]{ + {1,0}}; + expected = 0; + Assert.assertEquals(expected, uniquePathsWithObstacles(obstacleGrid)); + + obstacleGrid = new int[][]{ + {0,0}, + {0,1}}; + expected = 0; + Assert.assertEquals(expected, uniquePathsWithObstacles(obstacleGrid)); + + obstacleGrid = new int[][]{ + {0,0,0,0,0}, + {0,0,0,0,1}, + {0,0,0,1,0}, + {0,0,1,0,0}}; + expected = 0; + Assert.assertEquals(expected, uniquePathsWithObstacles(obstacleGrid)); + + } + + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + int m = obstacleGrid.length; + if (m == 0) { + return 0; + } + + int n = obstacleGrid[0].length; + if (obstacleGrid[m-1][n-1] == 1) { + return 0; + } + + int[][] dp = new int[m][n]; + + boolean blockFlag = false; + for (int i = 0; i < m; i++) { + if (obstacleGrid[i][0] == 1 || blockFlag) { + dp[i][0] = 0; + blockFlag = true; + } else { + dp[i][0] = 1; + } + } + + blockFlag = false; + for (int i = 0; i < n; i++) { + if (obstacleGrid[0][i] == 1 || blockFlag) { + dp[0][i] = 0; + blockFlag = true; + } else { + dp[0][i] = 1; + } + } + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (obstacleGrid[i][j] == 1){ + dp[i][j] = 0; + } else { + dp[i][j] = dp[i-1][j] + dp[i][j-1]; + } + } + } + + return dp[m-1][n-1]; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code100/E198_\346\211\223\345\256\266\345\212\253\350\210\215.java" "b/src/main/java/algorithm_practice/LeetCode/code100/E198_\346\211\223\345\256\266\345\212\253\350\210\215.java" new file mode 100644 index 00000000..446a38f9 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code100/E198_\346\211\223\345\256\266\345\212\253\350\210\215.java" @@ -0,0 +1,70 @@ +package algorithm_practice.LeetCode.code100; + +import org.junit.Assert; +import org.junit.Test; + +/* +你是一个专业的小偷,计划偷窃沿街的房屋。每间房内都藏有一定的现金,影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警。 + +给定一个代表每个房屋存放金额的非负整数数组,计算你 不触动警报装置的情况下 ,一夜之内能够偷窃到的最高金额。 + +  + +示例 1: + +输入:[1,2,3,1] +输出:4 +解释:偷窃 1 号房屋 (金额 = 1) ,然后偷窃 3 号房屋 (金额 = 3)。 +  偷窃到的最高金额 = 1 + 3 = 4 。 +示例 2: + +输入:[2,7,9,3,1] +输出:12 +解释:偷窃 1 号房屋 (金额 = 2), 偷窃 3 号房屋 (金额 = 9),接着偷窃 5 号房屋 (金额 = 1)。 +  偷窃到的最高金额 = 2 + 9 + 1 = 12 。 +  + +提示: + +0 <= nums.length <= 100 +0 <= nums[i] <= 400 + + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/house-robber +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class E198_打家劫舍 { + @Test + public void testCase() { + int[] nums = new int[]{1,2,3,1}; + int excepted = 4; + Assert.assertEquals(excepted, rob(nums)); + + nums = new int[]{2,7,9,3,1}; + excepted = 12; + Assert.assertEquals(excepted, rob(nums)); + + } + + public int rob(int[] nums) { + if (nums.length == 0) { + return 0; + } + + if (nums.length == 1) { + return nums[0]; + } + + // dp[i]: nums[0...i] 偷窃到的最高金额 + int[] dp = new int[nums.length]; + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + + for (int i = 2; i < nums.length; i++) { + dp[i] = Math.max(dp[i-1], dp[i-2] + nums[i]); + } + + return dp[nums.length-1]; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code200/M213_\346\211\223\345\256\266\345\212\253\350\210\2152.java" "b/src/main/java/algorithm_practice/LeetCode/code200/M213_\346\211\223\345\256\266\345\212\253\350\210\2152.java" new file mode 100644 index 00000000..4dfa71a6 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code200/M213_\346\211\223\345\256\266\345\212\253\350\210\2152.java" @@ -0,0 +1,94 @@ +package algorithm_practice.LeetCode.code200; + +import org.junit.Assert; +import org.junit.Test; + +/* +你是一个专业的小偷,计划偷窃沿街的房屋,每间房内都藏有一定的现金。这个地方所有的房屋都 围成一圈 ,这意味着第一个房屋和最后一个房屋是紧挨着的。同时,相邻的房屋装有相互连通的防盗系统,如果两间相邻的房屋在同一晚上被小偷闯入,系统会自动报警 。 + +给定一个代表每个房屋存放金额的非负整数数组,计算你 在不触动警报装置的情况下 ,能够偷窃到的最高金额。 + +  + +示例 1: + +输入:nums = [2,3,2] +输出:3 +解释:你不能先偷窃 1 号房屋(金额 = 2),然后偷窃 3 号房屋(金额 = 2), 因为他们是相邻的。 +示例 2: + +输入:nums = [1,2,3,1] +输出:4 +解释:你可以先偷窃 1 号房屋(金额 = 1),然后偷窃 3 号房屋(金额 = 3)。 +  偷窃到的最高金额 = 1 + 3 = 4 。 +示例 3: + +输入:nums = [0] +输出:0 +  + +提示: + +1 <= nums.length <= 100 +0 <= nums[i] <= 1000 + + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/house-robber-ii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class M213_打家劫舍2 { + @Test + public void testCase() { + int[] nums = new int[]{1, 2, 3, 1}; + int excepted = 4; + Assert.assertEquals(excepted, rob(nums)); + + nums = new int[]{2, 3, 2}; + excepted = 3; + Assert.assertEquals(excepted, rob(nums)); + + nums = new int[]{0}; + excepted = 0; + Assert.assertEquals(excepted, rob(nums)); + + nums = new int[]{1, 1}; + excepted = 1; + Assert.assertEquals(excepted, rob(nums)); + + nums = new int[]{200, 3, 140, 20, 10}; + excepted = 340; + Assert.assertEquals(excepted, rob(nums)); + + nums = new int[]{1, 3, 1, 3, 100}; + excepted = 103; + Assert.assertEquals(excepted, rob(nums)); + + } + + public int rob(int[] nums) { + int length = nums.length; + if (length == 1) { + return nums[0]; + } + if (length == 2) { + return Math.max(nums[0],nums[1]); + } + + return Math.max(rob(nums, 0, length - 2), + rob(nums, 1, length - 1)); + } + + private int rob(int[] nums, int begin, int end) { + int[] dp = new int[nums.length]; + dp[begin] = nums[begin]; + dp[begin+1] = Math.max(nums[begin], nums[begin+1]); + + for (int i = begin+2; i <= end; i++) { + dp[i] = Math.max(nums[i] + dp[i - 2], dp[i - 1]); + } + + return dp[end]; + } + +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code300/M337_\346\211\223\345\256\266\345\212\253\350\210\2153.java" "b/src/main/java/algorithm_practice/LeetCode/code300/M337_\346\211\223\345\256\266\345\212\253\350\210\2153.java" new file mode 100644 index 00000000..b056498d --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code300/M337_\346\211\223\345\256\266\345\212\253\350\210\2153.java" @@ -0,0 +1,63 @@ +package algorithm_practice.LeetCode.code300; + +import common.datastruct.TreeNode; +import common.util.ConstructBinaryTree; +import org.junit.Assert; +import org.junit.Test; + +/* +在上次打劫完一条街道之后和一圈房屋后,小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为“根”。 除了“根”之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫,房屋将自动报警。 + +计算在不触动警报的情况下,小偷一晚能够盗取的最高金额。 + +示例 1: + +输入: [3,2,3,null,3,null,1] + + 3 + / \ + 2 3 + \ \ + 3 1 + +输出: 7 +解释: 小偷一晚能够盗取的最高金额 = 3 + 3 + 1 = 7. +示例 2: + +输入: [3,4,5,1,3,null,1] + +  3 + / \ + 4 5 + / \ \ + 1 3 1 + +输出: 9 +解释: 小偷一晚能够盗取的最高金额 = 4 + 5 = 9. + + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/house-robber-iii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class M337_打家劫舍3 { + + @Test + public void testCase() { + Integer[] bfsArray = new Integer[]{3,2,3,null,3,null,1}; + TreeNode root = ConstructBinaryTree.constructByBFSArray(bfsArray); + int excepted = 7; + Assert.assertEquals(excepted, rob(root)); + + bfsArray = new Integer[]{3, 4, 5, 1, 3, null, 1}; + root = ConstructBinaryTree.constructByBFSArray(bfsArray); + excepted = 9; + Assert.assertEquals(excepted, rob(root)); + + } + + public int rob(TreeNode root) { + + return 0; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code400/E496_\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\2401.java" "b/src/main/java/algorithm_practice/LeetCode/code400/E496_\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\2401.java" new file mode 100644 index 00000000..3c83d655 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code400/E496_\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\2401.java" @@ -0,0 +1,95 @@ +package algorithm_practice.LeetCode.code400; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.HashMap; +import java.util.Map; +import java.util.Stack; + +/* +给定两个 没有重复元素 的数组 nums1 和 nums2 ,其中nums1 是 nums2 的子集。找到 nums1 中每个元素在 nums2 中的下一个比其大的值。 + +nums1 中数字 x 的下一个更大元素是指 x 在 nums2 中对应位置的右边的第一个比 x 大的元素。如果不存在,对应位置输出 -1 。 + +  + +示例 1: + +输入: nums1 = [4,1,2], nums2 = [1,3,4,2]. +输出: [-1,3,-1] +解释: + 对于num1中的数字4,你无法在第二个数组中找到下一个更大的数字,因此输出 -1。 + 对于num1中的数字1,第二个数组中数字1右边的下一个较大数字是 3。 + 对于num1中的数字2,第二个数组中没有下一个更大的数字,因此输出 -1。 +示例 2: + +输入: nums1 = [2,4], nums2 = [1,2,3,4]. +输出: [3,-1] +解释: +  对于 num1 中的数字 2 ,第二个数组中的下一个较大数字是 3 。 + 对于 num1 中的数字 4 ,第二个数组中没有下一个更大的数字,因此输出 -1 。 +  + +提示: + +nums1和nums2中所有元素是唯一的。 +nums1和nums2 的数组大小都不超过1000。 + + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/next-greater-element-i +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class E496_下一个更大元素1 { + + @Test + public void testCase() { + int[] nums1 = new int[]{4,1,2}; + int[] nums2 = new int[]{1,3,4,2}; + int[] excepted = new int[]{-1,3,-1}; + Assert.assertArrayEquals(excepted, nextGreaterElement(nums1, nums2)); + + nums1 = new int[]{2,4}; + nums2 = new int[]{1,2,3,4}; + excepted = new int[]{3,-1}; + Assert.assertArrayEquals(excepted, nextGreaterElement(nums1, nums2)); + + nums1 = new int[]{1,3,5,2,4}; + nums2 = new int[]{6,5,4,3,2,1,7}; + excepted = new int[]{7,7,7,7,7}; + Assert.assertArrayEquals(excepted, nextGreaterElement(nums1, nums2)); + + } + + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + + Map nums2IndexMap = new HashMap<>(nums2.length); + for (int i = 0; i < nums2.length; i++) { + nums2IndexMap.put(nums2[i], i); + } + + // 单调栈核心代码 begin + int[] res = new int[nums2.length]; // nums2 每个数字的下一个最大数 + Stack stack = new Stack<>(); + for (int i = nums2.length-1; i >= 0; i--) { + + while (!stack.isEmpty() && stack.peek()<=nums2[i]) { + stack.pop(); + } + + res[i] = stack.isEmpty() ? -1 : stack.peek(); + stack.push(nums2[i]); + } + + // 单调栈核心代码 end + + int[] nums1Res = new int[nums1.length]; + for (int i = 0; i < nums1.length; i++) { + Integer index = nums2IndexMap.get(nums1[i]); + nums1Res[i] = res[index]; + } + return nums1Res; + } + +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code500/M503_\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\2402.java" "b/src/main/java/algorithm_practice/LeetCode/code500/M503_\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\2402.java" new file mode 100644 index 00000000..43aaab88 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code500/M503_\344\270\213\344\270\200\344\270\252\346\233\264\345\244\247\345\205\203\347\264\2402.java" @@ -0,0 +1,56 @@ +package algorithm_practice.LeetCode.code500; + +import org.junit.Assert; +import org.junit.Test; + +import java.util.Stack; + +/* +给定一个循环数组(最后一个元素的下一个元素是数组的第一个元素),输出每个元素的下一个更大元素。数字 x 的下一个更大的元素是按数组遍历顺序,这个数字之后的第一个比它更大的数,这意味着你应该循环地搜索它的下一个更大的数。如果不存在,则输出 -1。 + +示例 1: + +输入: [1,2,1] +输出: [2,-1,2] +解释: 第一个 1 的下一个更大的数是 2; +数字 2 找不到下一个更大的数; +第二个 1 的下一个最大的数需要循环搜索,结果也是 2。 +注意: 输入数组的长度不会超过 10000。 + + + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/next-greater-element-ii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class M503_下一个更大元素2 { + + @Test + public void testCase() { + int[] nums = new int[]{1,2,1}; + int[] excepted = new int[]{2,-1,2}; + Assert.assertEquals(excepted, nextGreaterElements(nums)); + + nums = new int[]{2,4}; + excepted = new int[]{1,-1}; + Assert.assertEquals(excepted, nextGreaterElements(nums)); + + + } + + public int[] nextGreaterElements(int[] nums) { + int n = nums.length; + int[] res = new int[n]; + Stack stack = new Stack<>(); + + for(int i=2*n-1; i>=0; i--) { + while(!stack.isEmpty() && stack.peek() <= nums[i%n]) { + stack.pop(); + } + + res[i%n] = stack.isEmpty() ? -1 : stack.peek(); + stack.push(nums[i%n]); + } + return res; + } +} diff --git "a/src/main/java/algorithm_practice/LeetCode/code900/H980_\344\270\215\345\220\214\350\267\257\345\276\2043.java" "b/src/main/java/algorithm_practice/LeetCode/code900/H980_\344\270\215\345\220\214\350\267\257\345\276\2043.java" new file mode 100644 index 00000000..7270e390 --- /dev/null +++ "b/src/main/java/algorithm_practice/LeetCode/code900/H980_\344\270\215\345\220\214\350\267\257\345\276\2043.java" @@ -0,0 +1,120 @@ +package algorithm_practice.LeetCode.code900; + +import org.junit.Assert; +import org.junit.Test; + +/* +在二维网格 grid 上,有 4 种类型的方格: + +1 表示起始方格。且只有一个起始方格。 +2 表示结束方格,且只有一个结束方格。 +0 表示我们可以走过的空方格。 +-1 表示我们无法跨越的障碍。 +返回在四个方向(上、下、左、右)上行走时,从起始方格到结束方格的不同路径的数目。 + +每一个无障碍方格都要通过一次,但是一条路径中不能重复通过同一个方格。 + +  + +示例 1: + +输入:[[1,0,0,0],[0,0,0,0],[0,0,2,-1]] +输出:2 +解释:我们有以下两条路径: +1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2) +2. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2) +示例 2: + +输入:[[1,0,0,0],[0,0,0,0],[0,0,0,2]] +输出:4 +解释:我们有以下四条路径: +1. (0,0),(0,1),(0,2),(0,3),(1,3),(1,2),(1,1),(1,0),(2,0),(2,1),(2,2),(2,3) +2. (0,0),(0,1),(1,1),(1,0),(2,0),(2,1),(2,2),(1,2),(0,2),(0,3),(1,3),(2,3) +3. (0,0),(1,0),(2,0),(2,1),(2,2),(1,2),(1,1),(0,1),(0,2),(0,3),(1,3),(2,3) +4. (0,0),(1,0),(2,0),(2,1),(1,1),(0,1),(0,2),(0,3),(1,3),(1,2),(2,2),(2,3) +示例 3: + +输入:[[0,1],[2,0]] +输出:0 +解释: +没有一条路能完全穿过每一个空的方格一次。 +请注意,起始和结束方格可以位于网格中的任意位置。 +  + +提示: + +1 <= grid.length * grid[0].length <= 20 + + +来源:力扣(LeetCode) +链接:https://leetcode-cn.com/problems/unique-paths-iii +著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。 + */ +public class H980_不同路径3 { + @Test + public void testCase() { + int[][] grid = new int[][]{{1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 2, -1}}; + int expected = 2; + Assert.assertEquals(expected, uniquePathsIII(grid)); + + grid = new int[][]{{1, 0, 0, 0}, {0, 0, 0, 0}, {0, 0, 0, 2}}; + expected = 4; + Assert.assertEquals(expected, uniquePathsIII(grid)); + + grid = new int[][]{{0, 1}, {2, 0}}; + expected = 0; + Assert.assertEquals(expected, uniquePathsIII(grid)); + + } + + /* + 1. 找出起点 + 2. 统计0的个数,确定回溯的结束条件 + 3. 开始回溯 + */ + public int uniquePathsIII(int[][] grid) { + + int step = 0; // 0的个数 + int startX = 0, startY = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == 1) { + startX = i; + startY = j; + continue; + } + + if (grid[i][j] == 0) { + step++; + } + } + } + + // 走完所有的grid[][]==0,走到grid[][]==2时,也要耗费1步。 + return dfs(grid, step+1, startX, startY); + } + + private int dfs(int[][] grid, int step, int x, int y) { + + if (x < 0 || y < 0 + || x >= grid.length || y >= grid[0].length + || grid[x][y] == -1) { + return 0; + } + + if (grid[x][y] == 2) { + return step == 0 ? 1 : 0; + } + + int res = 0; + + grid[x][y] = -1; + res += dfs(grid, step-1, x+1, y); + res += dfs(grid, step-1, x, y-1); + res += dfs(grid, step-1, x-1, y); + res += dfs(grid, step-1, x, y+1); + grid[x][y] = 0; + + return res; + } +} diff --git a/src/main/java/algorithm_practice/README.md b/src/main/java/algorithm_practice/README.md index a27f4872..5e441e99 100644 --- a/src/main/java/algorithm_practice/README.md +++ b/src/main/java/algorithm_practice/README.md @@ -15,6 +15,10 @@ ## 滑动窗口 - [M567_字符串的排列](./LeetCode/code500/M567_字符串的排列.java) +### 单调栈 + - [E496_下一个更大元素1](./LeetCode/code400/E496_下一个更大元素1.java) + - [M503_下一个更大元素2](./LeetCode/code500/M503_下一个更大元素2.java) + ## 字符串操作 split、正则匹配、 - [M014_最长公共前缀](./LeetCode/code000/M014_最长公共前缀.java) @@ -46,18 +50,26 @@ split、正则匹配、 ## 动态规划 -1. 基本问题: +1. 基本问题 - [M300_最长上升子序列](./LeetCode/code300/M300_最长上升子序列.java) - [M096_不同的二叉搜索树的数量](./LeetCode/code000/M096_不同的二叉搜索树的数量.java) - -2. 最优子结构: - [M322_零钱兑换](./LeetCode/code300/M322_零钱兑换.java) + - [E198_打家劫舍](./LeetCode/code100/E198_打家劫舍.java) + - [M213_打家劫舍2](./LeetCode/code200/M213_打家劫舍2.java) + - [M337_打家劫舍3](./LeetCode/code300/M337_打家劫舍3.java) + + - [M062_不同路径](./LeetCode/code000/M062_不同路径.java) + - [M063_不同路径2](./LeetCode/code000/M063_不同路径2.java) + - [H980_不同路径3](./LeetCode/code900/H980_不同路径3.java) 4. 经典问题 + - [M062_不同路径](./LeetCode/code000/M062_不同路径.java) + - [M063_不同路径2](./LeetCode/code000/M063_不同路径2.java) - [H072_编辑距离](./LeetCode/code000/H072_编辑距离.java) (可空间优化) - [H887_鸡蛋掉落](./LeetCode/code800/H887_鸡蛋掉落.java) + ### [背包问题](./LeetCode/动态规划/背包问题.md) ### [股票买卖问题](./LeetCode/动态规划/股票买卖问题.md) @@ -72,6 +84,8 @@ split、正则匹配、 - [M039_组合总和](./LeetCode/code000/M039_组合总和.java) - [M040_组合总和2](./LeetCode/code000/M040_组合总和2.java) - [M216_组合总和3](./LeetCode/code200/M216_组合总和3.java) + - [H980_不同路径3](./LeetCode/code900/H980_不同路径3.java) + ## BFS - [E104_二叉树的最大深度](./LeetCode/code100/E104_二叉树的最大深度.java)