此篇承接上文:
一. 简单介绍
1. 回溯算法简单介绍
见Java-算法-回溯<一>
2. 回溯算法一般模板
见Java-算法-回溯<一>
3. 回溯算法的树型转换
见Java-算法-回溯<一>
二. leetcode实战
1~11 见Java-算法-回溯<一><二>
12. leetcode47 全排列II
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
输入:nums = [1,1,2]
输出:
[[1,1,2],
[1,2,1],
[2,1,1]]
class Solution {
List<List<Integer>> ans = new ArrayList<>();
int[] used;
public List<List<Integer>> permuteUnique(int[] nums) {
List<Integer> path = new ArrayList<>();
Arrays.sort(nums);
used = new int[nums.length];
dfs(nums,path);
return ans;
}
public void dfs(int[] nums, List<Integer> path){
if(path.size() == nums.length){
ans.add(new ArrayList<Integer>(path));
return;
}
for(int i = 0; i < nums.length; i++){
if(used[i] == 1 || i > 0 && nums[i] == nums[i-1] && used[i-1] == 1) continue;
used[i] = 1;
path.add(nums[i]);
dfs(nums,path);
path.remove(path.size() -1);
used[i] = 0;
}
}
}
本题小结:(1)used[i] == 1限定了取过的不能再取
(2)i > 0 && nums[i] == nums[i-1]意味着在同层遇到了前后相同的情况
(3)used[i-1] == 1意味着在之前已经取过了,本次不用再取。
13. 剑指 Offer 38. 字符串的排列
输入一个字符串,打印出该字符串中字符的所有排列。
你可以以任意顺序返回这个字符串数组,但里面不能有重复元素。
输入:s = "abc"
输出:["abc","acb","bac","bca","cab","cba"]
输入:
"aab"
输出:
["ab","ba"]
预期结果:
["aba","aab","baa"]
元素是可以重复的,不必进行过滤。
过滤版本
class Solution {
ArrayList<String> list = new ArrayList<>();
public String[] permutation(String s) {
HashSet<Character> set = new HashSet<>();
for(int i = 0; i < s.length(); i++){
set.add(s.charAt(i));
}
String str = "";
for(char c : set) str += c;
dfs(str,0,"");
String[] strarr = new String[list.size()];
for(int i = 0; i < list.size(); i++){
strarr[i] = list.get(i);
}
return strarr;
}
public void dfs(String str, int index, String temp){
if(temp.length() == str.length()){
list.add(temp);
return;
}
for(int i = 0; i < str.length(); i++){
String v1 = str.substring(i,i+1);
if(temp.contains(v1)) continue;
temp += v1;
dfs(str,i+1,temp);
temp = temp.substring(0,temp.length()-1);
}
}
}
非过滤版本
class Solution {
ArrayList<String> list = new ArrayList<>();
int[] used;
public String[] permutation(String s) {
used = new int[s.length()];
char[] c = s.toCharArray();
Arrays.sort(c);
String str = "";
for(char tmp:c) str += tmp;
dfs(str,"");
String[] strarr = new String[list.size()];
for(int i = 0; i < list.size(); i++){
strarr[i] = list.get(i);
}
return strarr;
}
public void dfs(String str, String temp){
if(temp.length() == str.length()){
list.add(temp);
return;
}
for(int i = 0; i < str.length(); i++){
if(used[i] == 1 || (i > 0 && str.charAt(i) == str.charAt(i-1)) && used[i-1] == 1 ) continue;
String v1 = str.substring(i,i+1);
temp += v1;
used[i] = 1;
dfs(str,temp);
temp = temp.substring(0,temp.length()-1);
used[i] = 0;
}
}
}
本题小结:(1)全排列,且有重复元素,注意过滤条件,同12
(2)由Arrays.toString的字符串会多出来,【和】,奇奇怪怪不要用,直接拼接
14. leetcode494 目标和
给你一个整数数组 nums 和一个整数 target 。
向数组中的每个整数前添加 '+' 或 '-' ,然后串联起所有整数,可以构造一个 表达式 :
例如,nums = [2, 1] ,可以在 2 之前添加 '+' ,在 1 之前添加 '-' ,然后串联起来得到表达式 "+2-1" 。
返回可以通过上述方法构造的、运算结果等于 target 的不同 表达式 的数目。
输入:nums = [1,1,1,1,1], target = 3
输出:5
解释:一共有 5 种方法让最终目标和为 3 。
-1 + 1 + 1 + 1 + 1 = 3
+1 - 1 + 1 + 1 + 1 = 3
+1 + 1 - 1 + 1 + 1 = 3
+1 + 1 + 1 - 1 + 1 = 3
+1 + 1 + 1 + 1 - 1 = 3
class Solution {
int count = 0;
public int findTargetSumWays(int[] nums, int target) {
dfs(nums,0,0,target);
return count;
}
public void dfs(int[] nums, int index, int path, int target){
if(index == nums.length){
if(path == target){
count++;
}
return;
}
//选择
path += nums[index];
dfs(nums,index+1,path,target);
//回来,撤销,选则另一种方法
path -= nums[index];
path -= nums[index];
dfs(nums,index+1,path,target);
}
}
本题小结:(1)撤销时执行两次path -= nums[index]