采用DFS的思想,顺序无关。
递归三要素:递归的定义、递归的拆解、递归的出口。
(一)Subsets
https://leetcode.com/problems/subsets/description/
题目:给出一个数组(无重复元素),返回该数组的全部子集(子集的数字升序排列);
解答:回溯法、递归函数(详见代码)
先将数组排序,依次把nums里的数字先压进temp中,再存入res中,递归调用bfsHelper继续遍历当前i之后的nums元素,遍历完成后,将i从temp中删除,继续后续的遍历;
采用index标记:以避免重复子集
第一次犯错:list是动态的,即使在添加到结果之后删除list中的某个元素,结果会动态显示删除元素后的list。因此需要将当前的list的复制版本加入到结果中。
代码:
public class Solution {
/**
* @param nums: A set of numbers
* @return: A list of lists
*/
public List<List<Integer>> subsets(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> temp = new ArrayList<>();
//add empty set
res.add(temp);
if (nums == null || nums.length == 0) {
return res;
}
//保证子集数组升序排列
Arrays.sort(nums);
bfsHelper(nums, res, temp, 0);
return res;
}
private void bfsHelper(int[] nums, List<List<Integer>> res, List<Integer> temp, int index){
for (int i = index; i < nums.length; i++) {
temp.add(nums[i]);
//need to wrap temp as a new ArrayList
res.add(new ArrayList<Integer>(temp));
bfsHelper(nums, res, temp, i + 1);
temp.remove(temp.size() - 1);
}
}
}
(二)Combination Sum
https://leetcode.com/problems/combination-sum/description/
题目:给出一个候选数组、一个目标值,要求返回由候选数组的数组成的所有和为目标值的组合(元素可重复);
解答:与subsets思想类似,注意input数组中元素不再distinct,遇到重复元素需要跳过以避免添加重复子集;
代码:
public class Solution {
/**
* @param candidates: A list of integers
* @param target: An integer
* @return: A list of lists of integers
*/
public List<List<Integer>> combinationSum(int[] candidates, int target) {
// write your code here
List<List<Integer>> res = new ArrayList<>();
List<Integer> temp = new ArrayList<>();
if (candidates == null || candidates.length == 0) {
return res;
}
Arrays.sort(candidates);
dfsHelper(candidates, target, res, temp, 0);
return res;
}
private void dfsHelper(int[] candidates, int target, List<List<Integer>> res, List<Integer> temp, int index) {
for (int i = index; i < candidates.length; i++) {
//remove duplicated element
if (i > 0 && candidates[i] == candidates[i - 1]) {
continue;
}
temp.add(candidates[i]);
if (getSum(temp) < target) {
dfsHelper(candidates, target, res, temp, i);
} else if (getSum(temp) == target) {
res.add(new ArrayList<>(temp));
}
temp.remove(temp.size() - 1);
}
}
private int getSum(List<Integer> list) {
int sum = 0;
for (int i = 0; i < list.size(); i++) {
sum += list.get(i);
}
return sum;
}
}
(三)Subsets||
https://leetcode.com/problems/subsets-ii/description/
题目:与Subsets相似,但是给出的数组有重复元素,返回的子集里不可有重复元素;
解答:与subsets类型,但是不可将重复的元素放入栈中;
代码:(与subsets不同部分,添加变量lastAdded记录上一次放入栈中的数字,避免重复)
private void dfsMethod (List<List<Integer>> list, int[] nums, List<Integer> singleList, int index) {
int lastAdded = 0;
if (index < nums.length) {
singleList.add(nums[index]);
list.add(new ArrayList<Integer>(singleList));
dfsMethod(list, nums, singleList, index + 1);
singleList.remove(singleList.size() - 1);
lastAdded = nums[index];
}
for (int i = index + 1; i < nums.length; i++) {
if (nums[i] == lastAdded) {
continue;
}
singleList.add(nums[i]);
list.add(new ArrayList<Integer>(singleList));
dfsMethod(list, nums, singleList, i + 1);
singleList.remove(singleList.size() - 1);
lastAdded = nums[i];
}
}
(四)Palindrome Partitioning
https://leetcode.com/problems/palindrome-partitioning/description/
题目:将给定字符串拆分成每个元素均回文的集合,返回所有组合。例如:给定”aab",返回[["a","a","b"], ["aa", "b"]]。
解答:从头开始拆分,遇到回文的字符串,则将其放入栈,再利用递归继续寻找后续回文字符串。若最后一个元素所在字符串依然满足回文,则加入最终结果。
第一次犯错:list是动态的,即使在添加到结果之后删除list中的某个元素,结果会动态显示删除元素后的list。因此需要将当前的list的复制版本加入到结果中;
第二次犯错:忘记“递归的出口”:list.remove();
第三次犯错:遇到不回文的字符串时,不应该立即中断for循环,因为虽然"ef"不回文,但是“efe”仍然回文.
改进:可以将dfsMethod()函数中的curString参数舍弃,因为可以用index参数表示:String subString = s.substring(index, i + 1)
代码:
class Solution {
public List<List<String>> partition(String s) {
List<List<String>> res = new ArrayList<>();
List<String> singleList = new ArrayList<>();
if (s == null || s.length() == 0) {
return res;
}
dfsMethod(res, singleList, s, 0, "");
return res;
}
private void dfsMethod(List<List<String>> res, List<String> singleList, String s, int index, String curString) {
for (int i = index; i < s.length(); i++) {
curString += s.charAt(i);
if (judgePalindrome(curString)) {
singleList.add(curString);
if (i == s.length() - 1) {
res.add(new ArrayList<String>(singleList));
break;
}
dfsMethod(res, new ArrayList<String>(singleList), s, i + 1, "");
singleList.remove(singleList.size() - 1);
}
}
}
private Boolean judgePalindrome(String curString) {
if (curString.length() == 0) {
return false;
}
for (int i = 0; i < curString.length() / 2; i++) {
if (curString.charAt(i) != curString.charAt(curString.length() - i - 1)) {
return false;
}
}
return true;
}
}