问题 数字 n
代表生成括号的对数,请你设计一个函数,用于能够生成所有可能的并且 有效的 括号组合。
示例1:
1 2 输入:n = 3 输出:["((()))" ,"(()())" ,"(())()" ,"()(())" ,"()()()" ]
示例2:
提示:
1 <= n <= 8
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { public List<String> generateParenthesis (int n) { List<String> result = new ArrayList <>(); backtracking(n, result, 0 , 0 , "" ); return result; } private static void backtracking (int n, List<String> result, int left, int right, String str) { if (right > left) { return ; } if (left == right && right == n) { result.add(str); return ; } if (left < n) { backtracking(n, result, left + 1 , right, str + "(" ); } if (right < left) { backtracking(n, result, left, right + 1 , str + ")" ); } } }
问题 给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
示例1:
1 2 3 4 5 6 7 8 9 10 输入:n = 4 , k = 2 输出: [ [2 ,4 ], [3 ,4 ], [2 ,3 ], [1 ,2 ], [1 ,3 ], [1 ,4 ], ]
示例2:
1 2 输入:n = 1 , k = 1 输出:[[1 ]]
提示:
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 class Solution { private List<List<Integer>> result = new ArrayList <>(); private List<Integer> path = new ArrayList <>(); public List<List<Integer>> combine (int n, int k) { backtracking(n, k, 1 ); return result; } private void backtracking (int n, int k, int startIndex) { if (path.size() == k) { result.add(new ArrayList <>(path)); return ; } for (int i = startIndex; i <= n; i++) { path.add(i); backtracking(n, k, i + 1 ); path.remove(path.size() - 1 ); } } }
剪枝优化 搜索起点的上界 + 接下来要选择的元素个数 - 1 = n 其中,接下来要选择的元素个数 = k - path.size(),整理得到:
搜索起点的上界 = n - (k - path.size()) + 1 所以,我们的剪枝过程就是:把 i <= n 改成 i <= n - (k - path.size()) + 1 :
1 2 3 4 5 for (int i = startIndex; i <= n - (k - path.size()) + 1 ; i++) { path.add(i); backtracking(n, k, i + 1 ); path.remove(path.size() - 1 ); }
问题 找出所有相加之和为 n 的 k 个数的组合,且满足下列条件:
只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例1:
1 2 3 4 5 输入: k = 3 , n = 7 输出: [[1 ,2 ,4 ]] 解释: 1 + 2 + 4 = 7 没有其他符合的组合了。
示例2:
1 2 3 4 5 6 7 输入: k = 3 , n = 9 输出: [[1 ,2 ,6 ], [1 ,3 ,5 ], [2 ,3 ,4 ]] 解释: 1 + 2 + 6 = 9 1 + 3 + 5 = 9 2 + 3 + 4 = 9 没有其他符合的组合了。
示例3:
1 2 3 4 输入: k = 4 , n = 1 输出: [] 解释: 不存在有效的组合。 在[1 ,9 ]范围内使用4 个不同的数字,我们可以得到的最小和是1 +2 +3 +4 = 10 ,因为10 > 1 ,没有有效的组合。
提示:
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { private List<Integer> path = new ArrayList <>(); private List<List<Integer>> result = new ArrayList <>(); public List<List<Integer>> combinationSum3 (int k, int n) { backtracking(k, n, 1 , 0 ); return result; } private void backtracking (int k, int n, int startIndex, int sum) { if (path.size() == k) { if (sum == n) { result.add(new ArrayList <>(path)); return ; } } for (int i = startIndex; i <= 9 ; i++) { sum += i; path.add(i); backtracking(k, n, i + 1 , sum); sum -= i; path.remove(path.size() - 1 ); } } }
剪枝优化: 1 2 3 4 5 6 7 8 9 if (sum > n) { return ; } if (path.size() == k) { if (sum == n) { result.add(new ArrayList <>(path)); return ; } }
问题 给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例1:
1 2 输入:digits = "23" 输出:["ad" ,"ae" ,"af" ,"bd" ,"be" ,"bf" ,"cd" ,"ce" ,"cf" ]
示例2:
示例3:
1 2 输入:digits = "2" 输出:["a" ,"b" ,"c" ]
提示:
0 <= digits.length <= 4
digits[i]
是范围 ['2', '9']
的一个数字。
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class Solution { private final String[] letterMap = { "" , "" , "abc" , "def" , "ghi" , "jkl" , "mno" , "pqrs" , "tuv" , "wxyz" }; private List<String> result = new ArrayList <>(); public List<String> letterCombinations (String digits) { if ("" .equals(digits)) { return result; } backtracking(digits, 0 , new StringBuilder ()); return result; } private void backtracking (String digits, int index, StringBuilder subString) { if (index == digits.length()) { result.add(subString.toString()); return ; } int digit = digits.charAt(index) - '0' ; String letters = letterMap[digit]; for (int i = 0 ; i < letters.length(); i++) { subString.append(letters.charAt(i)); backtracking(digits, index + 1 , subString); subString.delete(subString.length() - 1 , subString.length()); } } }
问题 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target 的不同组合数少于 150 个。
示例1:
1 2 3 4 5 6 输入:candidates = [2 ,3 ,6 ,7 ], target = 7 输出:[[2 ,2 ,3 ],[7 ]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。7 也是一个候选, 7 = 7 。仅有这两种组合。
示例2:
1 2 输入: candidates = [2 ,3 ,5 ], target = 8 输出: [[2 ,2 ,2 ,2 ],[2 ,3 ,3 ],[3 ,5 ]]
示例3:
1 2 输入: candidates = [2 ], target = 1 输出: []
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 class Solution { public List<List<Integer>> combinationSum (int [] candidates, int target) { List<List<Integer>> result = new ArrayList <>(); Arrays.sort(candiates); backtracking(result, new ArrayList <>(), candidates, target, 0 , 0 ); return result; } private void backtracking (List<List<Integer>> res, List<Integer> path, int [] candidates, int target, int startIndex, int sum) { if (sum > target) { return ; } if (sum == target) { res.add(new ArrayList <>(path)); return ; } for (int i = startIndex; i < candidates.length; i++) { sum += candidates[i]; path.add(candidates[i]); backtracking(res, path, candidates, target, i, sum); sum -= candidates[i]; path.remove(path.size() - 1 ); } } }
剪枝优化 1 2 3 4 5 6 7 8 9 10 11 12 13 for (int i = startIndex; i < candidates.length && sum + candidates[i] <= target; i++) { sum += candidates[i]; path.add(candidates[i]); backtracking(res, path, candidates, target, i, sum); sum -= candidates[i]; path.remove(path.size() - 1 ); }
问题 给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例1:
1 2 3 4 5 6 7 8 输入: candidates = [10 ,1 ,2 ,7 ,6 ,1 ,5 ], target = 8 , 输出: [ [1 ,1 ,6 ], [1 ,2 ,5 ], [1 ,7 ], [2 ,6 ] ]
示例2:
1 2 3 4 5 6 输入: candidates = [2 ,5 ,2 ,1 ,2 ], target = 5 , 输出: [ [1 ,2 ,2 ], [5 ] ]
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 class Solution { public List<List<Integer>> combinationSum2 (int [] candidates, int target) { List<List<Integer>> res = new ArrayList <>(); Arrays.sort(candidates); backtracking(candidates, target, res, new ArrayList <Integer>(), 0 , new boolean [candidates.length], 0 ); return res; } private void backtracking (int [] candidates, int target, List<List<Integer>> res, List<Integer> path, int startIndex, boolean [] used, int sum) { if (sum > target) { return ; } if (sum == target) { res.add(new ArrayList <>(path)); return ; } for (int i = startIndex; i < candidates.length; i++) { if (i > 0 && candidates[i] == candidates[i - 1 ] && used[i - 1 ] == false ) { continue ; } path.add(candidates[i]); used[i] = true ; backtracking(candidates, target, res, path, i + 1 , used, sum + candidates[i]); path.remove(path.size() - 1 ); used[i] = false ; } } }
剪枝优化 1 2 3 4 5 6 7 8 for (int i = startIndex; i < candidates.length && sum + candidates[i] <= target; i++) { }
另一种代码 使用 i > startIndex
巧妙 设计 成 深度 可重复 数字, 横向 不允许 重复数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 class Solution { public List<List<Integer>> combinationSum2 (int [] candidates, int target) { List<List<Integer>> res = new ArrayList <>(); Arrays.sort(candidates); backtracking(candidates, target, res, new ArrayList <Integer>(), 0 , 0 ); return res; } private void backtracking (int [] candidates, int target, List<List<Integer>> res, List<Integer> path, int startIndex, int sum) { if (sum == target) { res.add(new ArrayList <>(path)); return ; } for (int i = startIndex; i < candidates.length && sum + candidates[i] <= target; i++) { if (i > startIndex && candidates[i] == candidates[i - 1 ]) { continue ; } path.add(candidates[i]); backtracking(candidates, target, res, path, i + 1 , sum + candidates[i]); path.remove(path.size() - 1 ); } } }
问题 给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。
回文串 是正着读和反着读都一样的字符串。
示例1:
1 2 输入:s = "aab" 输出:[["a" ,"a" ,"b" ],["aa" ,"b" ]]
示例2:
提示:
1 <= s.length <= 16
s
仅由小写英文字母组成
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 class Solution { public List<List<String>> partition (String s) { List<List<String>> res = new ArrayList <>(); backtracking(res, new ArrayList <String>(), s, 0 ); return res; } private void backtracking (List<List<String>> res, List<String> path, String s, int startIndex) { if (startIndex == s.length()) { res.add(new ArrayList <>(path)); return ; } for (int i = startIndex; i < s.length(); i++) { if (isPartition(s, startIndex, i)) { String substring = s.substring(startIndex, i + 1 ); path.add(substring); } else { continue ; } backtracking(res, path, s, i + 1 ); path.remove(path.size() - 1 ); } } private boolean isPartition (String s, int start, int end) { for (int i = start,j = end; i < j; i++, j--) { if (s.charAt(i) != s.charAt(j)) { return false ; } } return true ; } }
问题 有效 IP 地址 正好由四个整数(每个整数位于 0 到 255 之间组成,且不能含有前导 0),整数之间用 ‘.’ 分隔。
例如:”0.1.2.201” 和 “192.168.1.1” 是 有效 IP 地址,但是 “0.011.255.245”、”192.168.1.312” 和 “192.168@1.1 “ 是 无效 IP 地址。 给定一个只包含数字的字符串 s ,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s 中插入 ‘.’ 来形成。你 不能 重新排序或删除 s 中的任何数字。你可以按 任何 顺序返回答案。
示例1:
1 2 输入:s = "25525511135" 输出:["255.255.11.135" ,"255.255.111.35" ]
示例2:
1 2 输入:s = "0000" 输出:["0.0.0.0" ]
示例3:
1 2 输入:s = "101023" 输出:["1.0.10.23" ,"1.0.102.3" ,"10.1.0.23" ,"10.10.2.3" ,"101.0.2.3" ]
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 class Solution { public List<String> restoreIpAddresses (String s) { List<String> res = new ArrayList <>(); backtracking(res, new StringBuilder (), s, 0 , 0 ); return res; } private void backtracking (List<String> res, StringBuilder substring, String s, int startIndex, int pointCount) { if (startIndex == s.length() && pointCount == 4 ) { String withPointString = substring.toString(); res.add(withPointString.substring(0 , withPointString.length() - 1 )); return ; } for (int i = startIndex; i < s.length() && pointCount < 4 && i - startIndex < 3 ; i++) { if ((i > startIndex && s.charAt(startIndex) == '0' )) { continue ; } String str = s.substring(startIndex, i + 1 ); if (Integer.parseInt(str) >= 0 && Integer.parseInt(str) <= 255 ) { substring.append(str).append("." ); } else { continue ; } backtracking(res, substring, s, i + 1 , pointCount + 1 ); substring.deleteCharAt(substring.lastIndexOf("." )); substring.delete(substring.lastIndexOf("." ) + 1 , substring.length()); } } }
问题 给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例1:
1 2 输入:nums = [1 ,2 ,3 ] 输出:[[],[1 ],[2 ],[1 ,2 ],[3 ],[1 ,3 ],[2 ,3 ],[1 ,2 ,3 ]]
示例2:
1 2 输入:nums = [0 ] 输出:[[],[0 ]]
提示:
1 <= nums.length <= 10
-10 <= nums[i] <= 10
nums
中的所有元素 互不相同
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { public List<List<Integer>> subsets (int [] nums) { List<List<Integer>> res = new ArrayList <>(); backtracking(res, new ArrayList <Integer>(), nums, 0 ); return res; } private void backtracking (List<List<Integer>> res, List<Integer> path ,int [] nums, int startIndex) { res.add(new ArrayList <>(path)); if (startIndex == nums.length) { return ; } for (int i = startIndex; i < nums.length; i++) { path.add(nums[i]); backtracking(res, path, nums, i + 1 ); path.remove(path.size() - 1 ); } } }
模板 1 2 3 4 result.push_back(path); if (startIndex >= nums.size()) { return ; }
问题 给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例1:
1 2 输入:nums = [1 ,2 ,2 ] 输出:[[],[1 ],[1 ,2 ],[1 ,2 ,2 ],[2 ],[2 ,2 ]]
示例2:
1 2 输入:nums = [0 ] 输出:[[],[0 ]]
提示
1 <= nums.length <= 10
-10 <= nums[i] <= 10
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public List<List<Integer>> subsetsWithDup (int [] nums) { Arrays.sort(nums); List<List<Integer>> res = new ArrayList <>(); backtracking(res, new ArrayList <Integer>(), nums, 0 ); return res; } private void backtracking (List<List<Integer>> res, List<Integer> path, int [] nums, int startIndex) { res.add(new ArrayList <>(path)); if (startIndex == nums.length) { return ; } for (int i = startIndex; i < nums.length; i++) { if (i > startIndex && nums[i] == nums[i - 1 ]) { continue ; } path.add(nums[i]); backtracking(res, path, nums, i + 1 ); path.remove(path.size() - 1 ); } } }
问题 给你一个整数数组 nums ,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
示例1:
1 2 输入:nums = [4 ,6 ,7 ,7 ] 输出:[[4 ,6 ],[4 ,6 ,7 ],[4 ,6 ,7 ,7 ],[4 ,7 ],[4 ,7 ,7 ],[6 ,7 ],[6 ,7 ,7 ],[7 ,7 ]]
示例2:
1 2 输入:nums = [4 ,4 ,3 ,2 ,1 ] 输出:[[4 ,4 ]]
提示
1 <= nums.length <= 15
-100 <= nums[i] <= 100
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { public List<List<Integer>> findSubsequences (int [] nums) { List<List<Integer>> res = new ArrayList <>(); backtracking(res, new ArrayList <Integer>(), nums, 0 ); return res; } private void backtracking (List<List<Integer>> res, List<Integer> path, int [] nums, int startIndex) { if (path.size() > 1 ) { res.add(new ArrayList <>(path)); } Set<Integer> uset = new HashSet <>(); for (int i = startIndex; i < nums.length; i++) { if (path.size() > 0 && nums[i] < path.get(path.size() - 1 ) || !uset.add(nums[i])) { continue ; } path.add(nums[i]); backtracking(res, path, nums, i + 1 ); path.remove(path.size() - 1 ); } } }
问题 给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例1:
1 2 输入:nums = [1 ,2 ,3 ] 输出:[[1 ,2 ,3 ],[1 ,3 ,2 ],[2 ,1 ,3 ],[2 ,3 ,1 ],[3 ,1 ,2 ],[3 ,2 ,1 ]]
示例2:
1 2 输入:nums = [0 ,1 ] 输出:[[0 ,1 ],[1 ,0 ]]
示例3:
提示:
1 <= nums.length <= 6
-10 <= nums[i] <= 10
nums
中的所有整数 互不相同
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class Solution { public List<List<Integer>> permute (int [] nums) { List<List<Integer>> res = new ArrayList <>(); backtracking(res, new ArrayList <Integer>(), nums, new boolean [nums.length]); return res; } private void backtracking (List<List<Integer>> res, List<Integer> path, int [] nums, boolean [] used) { if (path.size() == nums.length) { res.add(new ArrayList <>(path)); return ; } for (int i = 0 ; i < nums.length; i++) { if (used[i]) continue ; path.add(nums[i]); used[i] = true ; backtracking(res, path, nums, used); path.remove(path.size() - 1 ); used[i] = false ; } } }
问题 给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例1:
1 2 3 4 5 输入:nums = [1 ,1 ,2 ] 输出: [[1 ,1 ,2 ], [1 ,2 ,1 ], [2 ,1 ,1 ]]
示例2:
1 2 输入:nums = [1 ,2 ,3 ] 输出:[[1 ,2 ,3 ],[1 ,3 ,2 ],[2 ,1 ,3 ],[2 ,3 ,1 ],[3 ,1 ,2 ],[3 ,2 ,1 ]]
提示:
1 <= nums.length <= 8
-10 <= nums[i] <= 10
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class Solution { public List<List<Integer>> permuteUnique (int [] nums) { List<List<Integer>> res = new ArrayList <>(); backtracking(res, new ArrayList <Integer>(), nums, new boolean [nums.length]); return res; } private void backtracking (List<List<Integer>> res, List<Integer> path, int [] nums, boolean [] used) { if (path.size() == nums.length) { res.add(new ArrayList <>(path)); return ; } Set<Integer> uset = new HashSet <>(); for (int i = 0 ; i < nums.length; i++) { if (used[i] || !uset.add(nums[i])) continue ; path.add(nums[i]); used[i] = true ; backtracking(res, path, nums, used); path.remove(path.size() - 1 ); used[i] = false ; } } }
问题 给定二叉树的根节点 root
,返回所有左叶子之和。
示例1:
1 2 3 输入: root = [3 ,9 ,20 ,null,null,15 ,7 ] 输出: 24 解释: 在这个二叉树中,有两个左叶子,分别是 9 和 15 ,所以返回 24
示例2:
提示:
节点数在 [1, 1000]
范围内
-1000 <= Node.val <= 1000
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 class Solution { public int sumOfLeftLeaves (TreeNode root) { if (root == null ) { return 0 ; } return sumRecurision(root); } private int sumRecurision (TreeNode node) { if (node == null || node.left == null && node.right == null ) return 0 ; int sumLeft = sumRecurision(node.left); int sumRight = sumRecurision(node.right); if (node.left != null && node.left.left == null && node.left.right == null ) { return node.left.val + sumLeft + sumRight; } return sumLeft + sumRight; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Solution { public int sumOfLeftLeaves (TreeNode root) { if (root == null ) { return 0 ; } Stack<TreeNode> stack = new Stack <>(); stack.push(root); int sum = 0 ; while (!stack.empty()) { TreeNode node = stack.pop(); if (node.left != null && node.left.left == null && node.left.right == null ) { sum += node.left.val; } if (node.left != null ) stack.push(node.left); if (node.right != null ) stack.push(node.right); } return sum; } }
问题 给定一个二叉树的 根节点 root
,请找出该二叉树的 最底层 最左边 节点的值。
假设二叉树中至少有一个节点。
示例1:
1 2 输入: root = [2 ,1 ,3 ] 输出: 1
示例2:
1 2 输入: [1 ,2 ,3 ,4 ,null,5 ,6 ,null,null,7 ] 输出: 7
提示:
二叉树的节点个数的范围是 [1,104]
-231 <= Node.val <= 231 - 1
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class Solution { public int findBottomLeftValue (TreeNode root) { Queue<TreeNode> queue = new LinkedList <>(); queue.offer(root); TreeNode res = root; while (!queue.isEmpty()) { TreeNode node = queue.poll(); if (node == null ) { continue ; } res = node; queue.offer(node.right); queue.offer(node.left); } return res.val; } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class Solution { TreeNode res = null ; int max = 0 ; public int findBottomLeftValue (TreeNode root) { res = root; reversivion(root, 1 ); return res.val; } private void reversivion (TreeNode node, int depth) { if (node == null ) { return ; } if (node != null && max < depth) { max = depth; res = node; } reversivion(node.left, depth + 1 ); reversivion(node.right, depth + 1 ); } }