剑指 Offer 38. 字符串的排列
注意:并没有剪枝,是利用set去的重。
class Solution {
public String[] permutation(String s) {
char c[] = s.toCharArray();
Set<String> res = new HashSet<>();
backtrack(c,"",new boolean[c.length],res);
return res.toArray(new String[res.size()]);
}
private void backtrack(char[] chars,String temp,boolean[] visited,Set<String> res){
if(chars.length == temp.length()){
res.add(temp);
return;
}
for(int i=0;i<chars.length;++i){
if(visited[i])
continue;
visited[i] = true;
backtrack(chars,temp+chars[i],visited,res);
visited[i] = false;
}
}
}
回溯算法模板:
private void backtrack("原始参数") {
//终止条件(递归必须要有终止条件)
if ("终止条件") {
//一些逻辑操作(可有可无,视情况而定)
return;
}
for (int i = "for循环开始的参数"; i < "for循环结束的参数"; i++) {
//一些逻辑操作(可有可无,视情况而定)
//做出选择
//递归
backtrack("新的参数");
//一些逻辑操作(可有可无,视情况而定)
//撤销选择
}
}
473. 火柴拼正方形
思路:理解为四叉树并不合适,但是仍然利用四叉树的深度递归解决
class Solution {
public boolean makesquare(int[] matchsticks) {
int sum = 0;
for(int i:matchsticks){
sum+=i;
}
if((sum&3 )!= 0||sum == 0) return false;
return dfs(0,matchsticks,sum>>2,new int[4]);
}
public boolean dfs(int index,int sticks[],int target,int size[]){
if(index == sticks.length){
if(size[0] == size[1] && size[1] == size[2] && size[2] == size[3]) return true;
return false;
}
for(int i=0;i<size.length;++i){
if(size[i] + sticks[index]>target)
continue;
size[i]+=sticks[index];
if(dfs(index+1,sticks,target,size)){
return true;
}else{
size[i]-=sticks[index];
}
}
return false;
}
}
90. 子集 II
1.每个父节点 都有一个独立的set 保存子节点取得元素,用于去重
class Solution {
List<List<Integer>> res ;
List<Integer> path;
public List<List<Integer>> subsetsWithDup(int[] nums) {
res = new ArrayList<>();
path = new ArrayList<>();
Arrays.sort(nums);
dfs(nums,0);
return res;
}
private void dfs(int[] nums,int start){
res.add(new ArrayList<>(path));
Set<Integer> set = new HashSet<>();
for(int i=start;i<nums.length;++i){
if(set.contains(nums[i])) continue;
path.add(nums[i]);
set.add(nums[i]);
dfs(nums,i+1);
path.remove(path.size()-1);
}
}
}
官方解法
回溯的时候,用过的元素 visited[i]会变为false,同一层没有用过的也是false,由于数组排过序,所以
如果当前元素和前一个元素相同,而且前一个元素visited[i]为false,说明前一个相同的元素在当前层已经被用过了
class Solution {
private List<List<Integer>> ans;
private List<Integer> path;
public List<List<Integer>> subsetsWithDup(int[] nums) {
ans = new ArrayList<>();
path = new ArrayList<>();
// 首先排序,让相同的两个元素排到一起去,便于去重
Arrays.sort(nums);
int n = nums.length;
// 使用 visited 数组来记录哪一个元素在当前路径中被使用了
boolean[] visited = new boolean[n];
// 开始回溯
backtrace(nums, 0, visited, n);
return ans;
}
private void backtrace(int[] nums, int start, boolean[] visited, int n) {
// 首先加入当前路径
ans.add(new ArrayList<>(path));
// 从 start 开始遍历每一个元素,尝试加入路径中
for (int i = start; i < n; ++i) {
// 如果当前元素和前一个元素相同,而且前一个元素没有被访问,说明前一个相同的元素在当前层已经被用过了
if (i > 0 && nums[i - 1] == nums[i] && !visited[i - 1]) continue;
// 记录下来,用过了当前的元素
visited[i] = true;
path.add(nums[i]); // 放到路径中
backtrace(nums, i + 1, visited, n); // 向下一个递归
visited[i] = false; // 回溯
path.remove(path.size() - 1);
}
}
}
牛客:括号生成
给出n对括号,请编写一个函数来生成所有的由n对括号组成的合法组合。
例如,给出n=3,解集为:
“((()))”, “(()())”, “(())()”, “()()()”, “()(())”,
public ArrayList<String> generateParenthesis (int n) {
// write code here
ArrayList<String> res = new ArrayList<>();
func(res,"",n,n);
return res;
}
private void func( List<String> res,String s,int l,int r){
if(l ==0 && r==0){
res.add(s);
return ;
}
if(l<0 ) return ;
if(r<l) return ;
func(res,s+"(",l-1,r);
func(res,s+")",l,r-1);
}