删除线格式 简单的打印全排列:dfs,用标记数组used标记是否用过
package leetcode160;
public class queue {
static int count=0;
public static void main(String[] args) {
char[] chs= {'a','b','c'};
int[] used=new int[3];
char[] temp=new char[3];
dfs(0,chs,temp,used);
}
public static void dfs(int deep,char[] chs,char[] temp,int[] used)
{
if(deep==chs.length) {
count++;
System.out.println(count+"--"+String.valueOf(temp));
return ;
}
for(int i=0;i<chs.length;i++) {
if(used[i]==0) {
temp[deep]=chs[i];
used[i]=1;
dfs(deep+1,chs,temp,used);
used[i]=0;
}
}
}
}
46题的全排列,放在list里面的
class Solution {
List<List<Integer>> res=new ArrayList();
public List<List<Integer>> permute(int[] nums) {
List<Integer> temp=new ArrayList();
int[] used=new int[nums.length];
dfs(0,temp,nums,used);
return res;
}
public void dfs(int deep,List<Integer> temp,int[] nums,int[] used){
if(deep==nums.length){
res.add(temp);
return ;
}
for(int i=0;i<nums.length;i++){
if(used[i]==0){
temp.add(nums[i]);
used[i]=1;//标记已经用过,后续的dfs就不会使用了
dfs(deep+1,new ArrayList(temp),nums,used);//每次都要放一个新的temp进去,不能用一个,没一次逗用一个新的,当前temp当前有效
temp.remove(temp.size()-1);//这个不要忘了,因为temp已经变了,必须改回来
used[i]=0;//桶上
}
}
}
}
我感觉回溯和dfs是一样的,都是一起用的,或者说我感觉dfs更是一种方法,而回溯是个思想,例如dfs可以一直深入下去,而加上回溯可以遍历更多中可能,当然dfs也可以遍历更多的可能,那就是回溯咯
看到一句话:A general approach to backtracking questions in Java (Subsets, Permutations, Combination Sum, Palindrome Partioning),翻译就是回溯主要用于子集,排列,组合,回文
leetcode78
方法1:递归探索
class Solution {
List<List<Integer>> res=new ArrayList();
public List<List<Integer>> subsets(int[] nums) {
List<Integer> temp=new ArrayList();
digui(0,nums,temp);
return res;
}
public void digui(int deep,int[] nums,List temp){
if(deep==nums.length){
res.add(temp);
return ;
}
//有
temp.add(nums[deep]);
digui(deep+1,nums,new ArrayList(temp));
//没有
temp.remove(temp.size()-1);
digui(deep+1,nums,new ArrayList(temp));
}
}
方法二:回溯
class Solution {
List<List<Integer>> res=new ArrayList();
public List<List<Integer>> subsets(int[] nums) {
List<Integer> temp=new ArrayList();
if(nums.length==0||nums==null) return null;
Arrays.sort(nums);
dfs(0,nums,temp);
return res;
}
public void dfs(int start,int[] nums,List temp){
res.add(new ArrayList(temp));
for(int i=start;i<nums.length;i++){
temp.add(nums[i]);
dfs(i+1,nums,temp);
temp.remove(temp.size()-1);
}
}
}
47 全排列2
这个代码那个used[i-1]==0的条件应该改成==1,因为等于0,反正我是解释不通,。。。。但是确实可以,速度慢了一倍,==1可行,是因为排序好了,重复元素一定在一起,如果i和i-1相等,前面的没用过,那么在这个循环之前i-1一定用了(仅限当前递归的for循环,因为for是顺序循环),同一个递归表示一个位置,不能重复,所以continue。
class Solution {
List<List<Integer>> res=new ArrayList();
public List<List<Integer>> permuteUnique(int[] nums) {
int[] used=new int[nums.length];
Arrays.sort(nums);
dfs(0,nums,new ArrayList<Integer>(),used);
return res;
}
public void dfs(int deep,int[] nums,List temp,int[] used){
if(deep==nums.length){
res.add(temp);
return ;
}
for(int i=0;i<nums.length;i++){
if(used[i]==1) continue;
if(i>0&&nums[i]==nums[i-1]&&used[i-1]==1) continue;
temp.add(nums[i]);
used[i]=1;
dfs(deep+1,nums,new ArrayList(temp),used);
used[i]=0;
temp.remove(temp.size()-1);
}
}
}
方法2:交换法,这个其实就是递归尝试的完美体现,看这个链接全排列,其实就是先确定第一个位置,然后对后面的递归求全排列,依次类推。
class Solution {
List<List<Integer>> res=new ArrayList();
public List<List<Integer>> permuteUnique(int[] nums) {
dfs(0,nums);
return res;
}
public void dfs(int deep,int[] nums){
if(deep==nums.length-1){
List list=new ArrayList();
for(int i:nums){
list.add(i);
}
res.add(list);
}
Set<Integer> set=new HashSet();
for(int i=deep;i<nums.length;i++){
//if(deep!=i&&nums[deep]==nums[i]) continue;
//这个出错了,大问题,大问题,因为我只考虑了第一个和其他后面的元素换的时候,出现1就不换,但是我没考虑到这样的
//1,2,2,2, 这样的,会和2换3次,但是换3次是没有用的,只要换一次就行,只有元素不同,全排列才会不同的
//所有用set,根据插入成功确定是否是置换过的相同元素
if(!set.add(nums[i])) continue;
swap(nums,deep,i);
dfs(deep+1,nums);
swap(nums,deep,i);
}
}
public void swap(int[] nums,int i,int j){
int temp=nums[i];
nums[i]=nums[j];
nums[j]=temp;
}
}
leetcode39组合求和
非常常规的递归回溯,模板问题,和子集有相似之处,就是,7为根对吧,有2,3,4,6选择,然后跟新target之后,2有2,3,6,而3的选择是没有2的,因为2,3和3,2是一样的
class Solution {
List<List<Integer>> res=new ArrayList();
public List<List<Integer>> combinationSum(int[] candidates, int target) {
List temp=new ArrayList();
dfs(candidates,target,temp,0);
return res;
}
public void dfs(int[] candidates,int target,List temp,int deep){
if(target==0){
res.add(temp);
}
if(target<0) return ;
for(int i=deep;i<candidates.length;i++){
temp.add(candidates[i]);
dfs(candidates,target-candidates[i],new ArrayList(temp),i);
temp.remove(temp.size()-1);
}
}
}
leetcode40 组合求和2
有重复元素,所有元素也只能使用一次,而且所求的组合不能重复,这就要求很搞了,记住一定要先排序,只要有重复元素 就先排序,而且如何判断,如何跳过重复呢,就是看他前一个是否用过了(用过是递归层,而重复判断是平行层),因为排序好了,所以前一个如果没用过,那么平行层一定用了,这时候就挑,如果前一个被用了,说明递归层用了,这时候就可以用。
class Solution {
List<List<Integer>> res=new ArrayList();
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
Arrays.sort(candidates);
List temp=new ArrayList();
int[] used=new int[candidates.length];
dfs(candidates,target,used,temp,0);
return res;
}
public void dfs(int[] candidates,int target,int[] used,List temp,int deep){
if(target==0){
res.add(temp);
return ;
}
if(target<0) return ;
for(int i=deep;i<used.length;i++){
if(i>deep&&candidates[i]==candidates[i-1]&&used[i-1]==0) continue;
temp.add(candidates[i]);
used[i]=1;
dfs(candidates,target-candidates[i],used,new ArrayList(temp),i+1);
temp.remove(temp.size()-1);
used[i]=0;
}
}
}