Leetcode 39. 组合总和
题目链接: 组合总和
自己的思路:一开始自己想到了,但是在把path加入到res中的时候没有new一个对象;导致一直出错!!!!
正确思路:和组合总和III类似,只不过区别在于,可以使用重复的元素,而且每个数组的长度没有限制,这样每次进行递归的时候,传入的startindex是i就可以了,因为可以重复,我们只需要满足当前的和等于target时直接返回就可以了;还有比较重要的剪枝操作,先把数组排序,然后当cursum大于target的时候,直接后面的都可以不用递归了,因为是递增的数组;回溯三部曲:1、传入参数:数组、目标和、当前和以及起始索引;2、终止条件:当满足当前和等于目标和的时候,加入数组,然后返回;3、单层逻辑:考虑剪枝,当当前和大于target的时候,后面都可以不要了,把当前值加入到path中,然后cursum加上当前值,然后递归,然后path弹出最后一个元素,cursum再减去当前值!!!!
代码:
class Solution {
List<List<Integer>> res = new ArrayList<>();
LinkedList<Integer> path= new LinkedList<>();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
//排序用于剪枝
Arrays.sort(candidates);
backtracking(candidates,target,0,0);
return res;
}
public void backtracking(int[] candidates,int target,int cursum,int startindex){
if(cursum==target){
//记得一定要新new一个对象,因为java是值传递
res.add(new ArrayList(path));
return;
}
for (int i=startindex;i<candidates.length;i++){
//如果当前的和大于target,那么后面所有的都会大于target,所以直接剪枝(因为数组是递增的)
if (cursum+candidates[i]>target) break;
path.add(candidates[i]);
cursum+=candidates[i];
backtracking(candidates,target,cursum,i);
path.removeLast();
cursum-=candidates[i];
}
}
}
复杂度分析:
时间复杂度:
O
(
n
)
\mathcal{O}(n)
O(n)
空间复杂度:
O
(
1
)
\mathcal{O}(1)
O(1)
Leetcode 40. 组合总和 II
题目链接: 组合总和 II
自己的思路:没想到如何去去重!!!
正确思路:去重分为两种,一个是数层去重,一个是树枝去重;这个题分析出来应该是数层去重,即每个数组中可以使用相同数值的数,但是不可以出现重复的数组;数层去重就是要定义一个used数组,用来标记是否已经使用过一个数,如果说当前元素的前一个元素我们没有使用过,说明是数层,否则是树枝,所以我们去重的逻辑是,当前的数如果等于前一个数而且前一个数如果没有使用过,则直接continue,去重;回溯三部曲:1、传入参数:数组、目标和、当前和、起始索引以及used数组;2、终止条件:当满足当前和等于目标和的时候,加入数组,然后返回;3、单层逻辑:考虑剪枝,当当前和大于target的时候,后面都可以不要了;考虑去重,如果是树层重复,则去重;把当前值加入到path中,然后cursum加上当前值,然后递归,然后path弹出最后一个元素,cursum再减去当前值!!!!
代码:
class Solution {
Set<List<Integer>> res = new HashSet<>();
LinkedList<Integer> path = new LinkedList<>();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
//用来标记是否已经使用过某个数字
boolean[] used = new boolean[candidates.length];
Arrays.fill(used,false);
backtracking(candidates,target,0,0,used);
return new ArrayList<>(res);
}
public void backtracking(int[] candidates, int target,int cursum,int startindex,boolean[] used){
if (cursum==target){
res.add(new ArrayList(path));
return;
}
for (int i=startindex;i<candidates.length;i++){
//剪枝
if (cursum+candidates[i]>target) break;
//去重
if (i>0&&candidates[i]==candidates[i-1]&&!used[i-1]) continue;
used[i]=true;
path.add(candidates[i]);
cursum+=candidates[i];
backtracking(candidates,target,cursum,i+1,used);
used[i]=false;
path.removeLast();
cursum-=candidates[i];
}
}
}
复杂度分析:
时间复杂度:
O
(
n
)
\mathcal{O}(n)
O(n)
空间复杂度:
O
(
1
)
\mathcal{O}(1)
O(1)
Leetcode 131. 分割回文串
题目链接: 分割回文串
自己的思路:没想到,难点在于如何去切割回文串,没有想到按区间去切割!
正确思路:和组合一个思路;只不过在组合的基础上要切割子串并且判断是否是回文串,如果是回文串才会加入到数组中;回溯三部曲:1、传入参数:字符串以及其指向其字符位置的指针;2、终止条件:当指向其最后的位置的时候,将path加入,然后返回;3、单层逻辑:先对一段区间的字符串进行判断是否是回文串(区间是[startindex,i]),如果是则加入到path中,不是的话就会continue,指针向后指,然后进行递归回溯即可!!!
代码:
class Solution {
List<List<String>> res = new ArrayList<>();
LinkedList<String> path = new LinkedList<>();
public List<List<String>> partition(String s) {
backtracking(s,0);
return res;
}
public void backtracking(String s,int startindex){
if (startindex==s.length()){
res.add(new ArrayList(path));
return;
}
for (int i=startindex;i<s.length();i++){
//如果是回文则记录
if (ishuiwen(s,startindex,i)){
String str = s.substring(startindex,i+1);
path.add(str);
}else continue;
backtracking(s,i+1);
path.removeLast();
}
}
//判断是否是回文串
public boolean ishuiwen(String s,int left,int right){
while(left<=right){
if (s.charAt(left)!=s.charAt(right)) return false;
left++;
right--;
}
return true;
}
}
复杂度分析:
时间复杂度:
O
(
n
)
\mathcal{O}(n)
O(n)
空间复杂度:
O
(
1
)
\mathcal{O}(1)
O(1)