LeetCode 39组合总和
题目简析:
给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合
思路分析:
和前面的组合那题的做法是非常类似的,具体的内容都可以看注释,注意的一个点是,原数组是无序,然后“continue”的使用。
List<List<Integer>>ans = new ArrayList<>();
List<Integer>arr = new ArrayList<>();
int max = 0;
public List<List<Integer>> combinationSum(int[] candidates, int target) {
int len = candidates.length;
back(candidates,target,len,max,0);
return ans;
}
public void back(int []candidates,int target,int len,int max,int index){
//终止条件----总和等于target
if(max == target){
ans.add(new ArrayList<>(arr));
return;
}
//剪枝----组合内的数是按大小顺序排的,元素排序后若元素一样则视为一个
// 数组是无重复的数组,若找到了与target相等的,则后续一定再无匹配
// 传入下标,因为当前位置只能从它后面开始挑,而不能从它前面,否则就会重复
for (int i = index; i < len; i++) {
if((max + candidates[i]) > target){
//若加上之后比当前target大,那么就下一个,其实就是不看后面的数
//使用continue原因:因为原给定数组是无序的
//所以只能使用continue而不能用break
//除非先对给定数组进行排序
continue;
}
//因为可以重复选,所以不需要跳过
arr.add(candidates[i]);
max += candidates[i];
back(candidates,target,len,max,i);
arr.remove(arr.size()-1);
max -= candidates[i];
}
}
LeetCode 40组合总和II
题目简析:
给定一个候选人编号的集合 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合
思路分析:这几题思路都非常类似,而本题就多了一个“标记元素是否被使用数组”的使用,具体的就看注释即可
List<List<Integer>>ans = new ArrayList<>();
LinkedList<Integer> arr = new LinkedList<>();
boolean []used;
//维护记录当前总和的变量
int sum = 0;
//记录candidates长度
int len;
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
//可以重复选,但是每个数字只有一次选择,所以先排序
Arrays.sort(candidates);
len = candidates.length;
//而因为只能选一次,所以我们需要去重,避免相同的数字选择了被选过的
//使用一个数组来记录元素有无被选择
used = new boolean[len];
Arrays.fill(used,false);
//传入下标为0
back(candidates,target,0);
return ans;
}
public void back(int[] candidates,int target,int startIndex){
//终止条件
if(sum == target){
ans.add(new ArrayList<>(arr));
return;
}
for (int i = startIndex; i < len; i++) {
//思考如何去重合剪枝是关键
//很显然当sum + candidates[i] > target的时候就可以直接break了
//因为c数组已经经过了排序
if(sum + candidates[i] > target){
break;
}
//剪枝----用i-1而不要使用i+1,否则会跳过部分情况
//若used[i-1]被使用过,那就跳过
if (i > 0 && candidates[i] == candidates[i - 1] && !used[i - 1]) {
continue;
}
used[i] = true;
sum += candidates[i];
arr.add(candidates[i]);
// 每个节点仅能选择一次,所以从下一位开始
back(candidates, target, i + 1);
used[i] = false;
sum -= candidates[i];
arr.removeLast();
}
}
LeetCode 131分割回文串
题目简析:
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是 回文串 。返回 s
所有可能的分割方案。
思路分析:见注释
List<List<String>> lists = new ArrayList<>();
LinkedList<String> linkedList = new LinkedList<>();
int len;
public List<List<String>> partition(String s) {
len = s.length();
back(s,0);
return lists;
} //判断是否是回文串
public void back(String s, int startIndex){
//如果起始位置大于s的大小,说明找到了一组分割方案
//这里终止条件与前面的几个组合问题有不同
//因为传的是字符串,而startIndex与下面back参数i+1相关
//i若走完了字符串,则一定会产生回文串,因为s.length>=1
if (startIndex >= s.length()) {
lists.add(new ArrayList(linkedList));
return;
}
for (int i = startIndex; i < len; i++) {
//是回文
if (isPalindrome(s, startIndex,i)) {
//从里面截取,加入集合---substring左闭右开---因此i要+1
String str = s.substring(startIndex, i + 1);
linkedList.addLast(str);
}else{
//不是就直接跳过
continue;
}
//起始位置后移,保证不重复
back(s,i+1);
linkedList.removeLast();
}
}
public boolean isPalindrome(String s, int startIndex, int end) {
for (int i = startIndex, j = end; i < j; i++, j--) {
if (s.charAt(i) != s.charAt(j)) {
return false;
}
}
return true;
}