1.简而言之,一个集合里求组合就要用startIndex。
2.startIndex本身保证了组合的不同,需要求不同组合就要用startIndex;但从 i 开始还是从 i + 1 开始决定了组合元素能不能重复选。(39)
3.组内既不能重复选,也不能有重复组合,数组中还有重复元素,就要sort(40)
4.startIndex就是取了后面的不能取前面的所以保证不同的组合,但是排列不存在这个问题所以不用startIndex
5.切割就是组合
6.子集是收集路径的组合
112,113,257
combination 77,39,40,216,17
partitioning 131,93
subsets 78,90
permutations 46,47
51,37
491,332
112. 路径总和
简单
给你二叉树的根节点 root
和一个表示目标和的整数 targetSum
。判断该树中是否存在 根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum
。如果存在,返回 true
;否则,返回 false
。
叶子节点 是指没有子节点的节点。
class Solution {
public boolean hasPathSum(TreeNode root, int targetSum) {
if(root == null){
return false;
}
if(root.left == null && root.right == null){
return root.val == targetSum;
}
boolean left = hasPathSum(root.left,targetSum - root.val);
boolean right = hasPathSum(root.right,targetSum - root.val);
return left || right;
}
}
中等
给你二叉树的根节点 root
和一个整数目标和 targetSum
,找出所有 从根节点到叶子节点 路径总和等于给定目标和的路径。
叶子节点 是指没有子节点的节点。
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
dfs(root, targetSum);
return ans;
}
private void dfs(TreeNode root , int targetSum){
if(root == null){
return;
}
path.add(root.val);
if(root.left == null && root.right == null){
if(root.val == targetSum){
ans.add(new ArrayList(path));
}
}
dfs(root.left , targetSum - root.val);
dfs(root.right , targetSum - root.val);
path.remove(path.size()-1);
}
}
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> pathSum(TreeNode root, int targetSum) {
dfs(root, targetSum);
return ans;
}
private void dfs(TreeNode root , int targetSum){
if(root == null){
return;
}
path.add(root.val);
if(root.left == null && root.right == null){
if(root.val == targetSum){
ans.add(new ArrayList(path));
}
}
if(root.left != null){
dfs(root.left , targetSum - root.val);
path.remove(path.size()-1);
}
if(root.right != null){
dfs(root.right , targetSum - root.val);
path.remove(path.size()-1);
}
}
}
257. 二叉树的所有路径
简单
给你一个二叉树的根节点 root
,按 任意顺序 ,返回所有从根节点到叶子节点的路径。
叶子节点 是指没有子节点的节点。
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> res = new ArrayList<>();// 存最终的结果
if (root == null) {
return res;
}
List<Integer> paths = new ArrayList<>();// 作为结果中的路径
traversal(root, paths, res);
return res;
}
private void traversal(TreeNode root, List<Integer> paths, List<String> res) {
paths.add(root.val);// 前序遍历,中
// 遇到叶子结点
if (root.left == null && root.right == null) {
// 输出
StringBuilder sb = new StringBuilder();// StringBuilder用来拼接字符串,速度更快
for (int i = 0; i < paths.size() - 1; i++) {
sb.append(paths.get(i)).append("->");
}
sb.append(paths.get(paths.size() - 1));// 记录最后一个节点
res.add(sb.toString());// 收集一个路径
return;
}
// 递归和回溯是同时进行,所以要放在同一个花括号里
if (root.left != null) { // 左
traversal(root.left, paths, res);
paths.remove(paths.size() - 1);// 回溯
}
if (root.right != null) { // 右
traversal(root.right, paths, res);
paths.remove(paths.size() - 1);// 回溯
}
}
}
class Solution {
List<String> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<String> binaryTreePaths(TreeNode root) {
dfs(root);
return ans;
}
private void dfs(TreeNode root){
if(root == null){
return;
}
path.add(root.val);
if(root.left == null && root.right ==null){
StringBuilder sb = new StringBuilder();// StringBuilder用来拼接字符串,速度更快
for (int i = 0; i < path.size() - 1; i++) {
sb.append(path.get(i)).append("->");
}
sb.append(path.get(path.size() - 1));// 记录最后一个节点
ans.add(sb.toString());// 收集一个路径
}
dfs(root.left);
dfs(root.right);
path.remove(path.size()-1);
}
}
77. 组合
中等
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
示例 1:
输入:n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> combine(int n, int k) {
backTrack(n, k, 1);
return ans;
}
private void backTrack(int n, int k, int startIndex){
if(path.size() == k){
ans.add(new ArrayList(path));
return;
}
for(int i = startIndex; i <= n; i++){
path.add(i);
backTrack(n, k, i + 1);
path.remove(path.size()-1);
}
}
}
中等
给你一个 无重复元素 的整数数组 candidates
和一个目标整数 target
,找出 candidates
中可以使数字和为目标数 target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target
的不同组合数少于 150
个。
示例 1:
输入:candidates =[2,3,6,7],
target =7
输出:[[2,2,3],[7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
backTrack(candidates, target, 0);
return ans;
}
private void backTrack(int[] candidates, int target, int startIndex){
if(target < 0 ){
return;
}
if(target == 0){
ans.add(new ArrayList(path));
}
for(int i = startIndex; i < candidates.length; i++){
path.add(candidates[i]);
backTrack(candidates, target-candidates[i], i);
path.remove(path.size()-1);
}
}
}
中等
给定一个候选人编号的集合 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
示例 1:
输入: candidates =[10,1,2,7,6,1,5]
, target =8
, 输出: [ [1,1,6], [1,2,5], [1,7], [2,6] ]
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
backTrack(candidates,target,0);
return ans;
}
private void backTrack(int[] candidates, int target, int startIndex){
if(target == 0){
ans.add(new ArrayList(path));
return;
}
for(int i = startIndex; i < candidates.length; i++){
if(target < 0){
break;
}
if(i > startIndex && candidates[i] == candidates[i-1]){
continue;
}
path.add(candidates[i]);
backTrack(candidates, target - candidates[i], i + 1);
path.remove(path.size()-1);
}
}
}
中等
找出所有相加之和为 n
的 k
个数的组合,且满足下列条件:
- 只使用数字1到9
- 每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
示例 1:
输入: k = 3, n = 7 输出: [[1,2,4]] 解释: 1 + 2 + 4 = 7 没有其他符合的组合了。
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> combinationSum3(int k, int n) {
backTrack(k, n, 1, 0);
return ans;
}
private void backTrack(int k, int n, int startIndex, int sum){
if(path.size() == k){
if(sum == n){
ans.add(new ArrayList(path));
}
return;
}
for(int i = startIndex; i <= 9; i++){
path.add(i);
backTrack(k, n, i + 1, sum + i);
path.remove(path.size()-1);
}
}
}
中等
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例 1:
输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
示例 2:
输入:digits = "" 输出:[]
示例 3:
输入:digits = "2" 输出:["a","b","c"]
提示:
0 <= digits.length <= 4
digits[i]
是范围['2', '9']
的一个数字。
class Solution {
List<String> ans = new ArrayList<>();
StringBuilder path = new StringBuilder();
public List<String> letterCombinations(String digits) {
if (digits == null || digits.length() == 0) {
return ans;
}
Map<Character, String> phoneMap = new HashMap<Character, String>() {{
put('2', "abc");
put('3', "def");
put('4', "ghi");
put('5', "jkl");
put('6', "mno");
put('7', "pqrs");
put('8', "tuv");
put('9', "wxyz");
}};
backTrack(digits, phoneMap, 0);
return ans;
}
private void backTrack(String digits, Map<Character, String> phoneMap, int num) {
if (num == digits.length()) {
ans.add(path.toString());
return;
}
char ch = digits.charAt(num);
String str = phoneMap.get(ch);
for (int i = 0; i < str.length(); i++) {
path.append(str.charAt(i));
backTrack(digits, phoneMap, num + 1);
path.deleteCharAt(path.length() - 1);
}
}
}
中等
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是
回文串
。返回 s
所有可能的分割方案。
示例 1:
输入:s = "aab" 输出:[["a","a","b"],["aa","b"]]
class Solution {
List<List<String>> ans = new ArrayList<>();
List<String> path = new ArrayList<>();
public List<List<String>> partition(String s) {
backTrack(s, 0);
return ans;
}
private void backTrack(String s , int startIndex){
if(startIndex == s.length()){
ans.add(new ArrayList(path));
return;
}
for(int i = startIndex; i < s.length(); i++){
String str = s.substring(startIndex, i + 1);
if(isPalindrome(s, startIndex,i)){
path.add(str);
}else{
continue;
}
backTrack(s, i + 1);
path.remove(path.size() - 1);
}
}
private boolean isPalindrome(String s, int startIndex, int endIndex){
while(startIndex <= endIndex){
if(s.charAt(startIndex) != s.charAt(endIndex)){
return false;
}
startIndex++;
endIndex--;
}
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:
输入:s = "25525511135" 输出:["255.255.11.135","255.255.111.35"]
class Solution {
List<String> ans = new ArrayList<>();
StringBuilder path = new StringBuilder();
public List<String> restoreIpAddresses(String s) {
backTrack(s, 0 , 0);
return ans;
}
private void backTrack(String s , int startIndex ,int count){
if(count == 4 && startIndex == s.length()){
ans.add(path.deleteCharAt(path.length() - 1).toString());
return;
}
if(count > 4){
return;
}
for(int i = startIndex; i < s.length() && i < startIndex + 3;i++){
String str = s.substring(startIndex,i + 1);
if(isTrue(str)){
int len = path.length();
path.append(str).append(".");
backTrack(s, i + 1 , count + 1);
path.setLength(len);
}
}
}
private boolean isTrue(String s){
if(s.length() > 1 && s.charAt(0) == '0'){
return false;
}
int a = Integer.parseInt(s);
if(a > 255 || a < 0){
return false;
}
return true;
}
}
78. 子集
中等
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
示例 1:
输入:nums = [1,2,3] 输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsets(int[] nums) {
backTrack(nums, 0);
return ans;
}
private void backTrack(int[] nums, int startIndex) {
ans.add(new ArrayList<>(path));
if (startIndex == nums.length) {
return;
}
for (int i = startIndex; i < nums.length; i++) {
path.add(nums[i]);
backTrack(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
相关标签
相关企业
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的
子集
(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
示例 1:
输入:nums = [1,2,2] 输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
public List<List<Integer>> subsetsWithDup(int[] nums) {
Arrays.sort(nums);
backTrack(nums,0);
return ans;
}
private void backTrack(int[] nums, int startIndex) {
ans.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]);
backTrack(nums, i + 1);
path.remove(path.size() - 1);
}
}
}
中等
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
示例 1:
输入:nums = [1,2,3] 输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used;
public List<List<Integer>> permute(int[] nums) {
used = new boolean[nums.length];
backTrack(nums);
return ans;
}
private void backTrack(int[] nums) {
if (path.size() == nums.length) {
ans.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i] == false) {
path.add(nums[i]);
used[i] = true;
backTrack(nums);
path.remove(path.size() - 1);
used[i] = false;
}
}
}
}
中等
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
示例 1:
输入:nums = [1,1,2] 输出: [[1,1,2], [1,2,1], [2,1,1]]
class Solution {
List<List<Integer>> ans = new ArrayList<>();
List<Integer> path = new ArrayList<>();
boolean[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
used = new boolean[nums.length];
Arrays.sort(nums);
backTrack(nums);
return ans;
}
private void backTrack(int[] nums) {
if (path.size() == nums.length) {
ans.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (i > 0 && nums[i] == nums[i - 1] && used[i - 1] == false) {//注意条件
continue;
}
if (used[i] == false) {
path.add(nums[i]);
used[i] = true;
backTrack(nums);
used[i] = false;
path.remove(path.size() - 1);
}
}
}
}