leetcode之回溯backtracing专题2

来源:互联网 发布:阿里巴巴比淘宝贵2016 编辑:程序博客网 时间:2024/05/04 06:07

46 Permutations

 输入一个不重复的数组 ,写出这个数组的排列,不能重复。
 例如 输入nums=[1,2,3],输出
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

 思路:可以看到 第0位(从左开始数)有nums.length个数字可以选择:1,2,3,在第0位确定以后,第1位有nums.length-1个数字可以选择,例如p[0] = 1,p[1]只可能为2 或者3。所以重要的是记录哪些下标的元素已经被选择过。

public class Solution {    private boolean[] p;//记录选中过的下标    private List<List<Integer>> list;    public List<List<Integer>> permute(int[] nums) {        int[] newarray=new int[nums.length];//存放选中的数字        p = new boolean[nums.length];        list = new ArrayList<List<Integer>>();        robot(0,newarray,nums);        return list;    }    public void robot(int idx,int[] newarray,int[] nums){        if(idx>=nums.length){            List<Integer> l = new ArrayList<Integer>();            for(int i=0;i<newarray.length;i++){                l.add(newarray[i]);            }            list.add(l);            return;        }        for(int i=0;i<nums.length;i++){            if(p[i]==false){                newarray[idx]=nums[i];                p[i]=true;                robot(idx+1,newarray,nums);                p[i]=false;            }        }    }}

47 Permutations II

 输入一个重复的数组 ,写出这个数组的排列,不能重复。
 例如输入 [1,1,2] 输出
[
[1,1,2],
[1,2,1],
[2,1,1]
]
 思路:这与46的区别是输入的数组中有重复的数字。如果按照46的思路做,会出现重复的结果。我们先按照46的来做一下吧。用”数字(下标)”这种方式表示数组元素:1(0) 1(1) 2(2)。这样才能把相同的数字区分开来。为了方便,我们先将输入的数组排序。按照46的思路得到以下组合:
1(0) 1(1) 2(2)
1(0) 2(2) 1(1)
1(1) 1(0) 2(2)
1(1) 2(2) 1(0)
2(2) 1(0) 1(1)
2(2) 1(1) 1(0)
 重点标记的是重复的元素。分析一下。第0位可以选择的元素有 nums[0], nums[1], nums[2],但是当已经选择nums[0]之后,再遇到nums[1]=nums[0]的时候nums[1]是不能选择的,否则就重复了。所以第0位可以选择的元素有 nums[0], nums[2]。每一位都是,判断nums[idx]能不能选择标准是nums[idx-1]=nums[idx]是否为true。
这个判断怎么加呢?下面记录一下我自己犯过的错。

处理情况1

for(int i=0;i<nums.length;i++){          if(p[i]==false){              newarray[idx]=nums[i];              p[i]=true;              robot(idx+1,newarray,nums);              p[i]=false;              for (int j = i + 1; j < nums.length; j++){                  if (nums[j] != nums[j - 1]) {                      i = j - 1;                      break;                  }              }          }      }  

 每到一个位置,先选择一个元素,再处理下一个元素和前面的元素是否相同。这里判断的时候没有考虑前面的元素是否会选中,可能会有多余的操作。更重要的是没有处理如果重复元素是最后一个元素怎么处理。失败用例:[1,1]。
 

    for(int i=0;i<nums.length;i++){            if(p[i]==false){                newarray[idx]=nums[i];                p[i]=true;                robot(idx+1,newarray,nums);                p[i]=false;                int j = i + 1;                for (; j < nums.length; j++){                    if (nums[j] != nums[j - 1]) {                        i = j - 1;                        break;                    }                }                if(j==nums.length){                    break;                }            }        }

 改成这样就对了。但是这样代码不够优雅。

处理情况2

    for(int i=0;i<nums.length;i++){            if(p[i]==false && (i==0 || (nums[i]!=newarray[idx]))){                newarray[idx]=nums[i];                p[i]=true;                robot(idx+1,newarray,nums);                p[i]=false;            }        }  

 因为newarray[idx]记录了上一次选择的值,所以想到只要判断这次的候选值nums[i]和newarray[idx]不同就好了。这次的错误是因为i==0的判断是有误的。因为不一定每次都是nums[0]是第一个被选中的。例如当第0位选择nums[0],在选第2位的时候第一个被选中的一定是nums[1] ,所以这里应该用一个boolean变量表示本次是不是对idx已经选过一个值了。
 

        boolean selected = false;        for(int i=0;i<nums.length;i++){            if(p[i]==false && (!selected || (nums[i]!=newarray[idx]))){                selected = true;                newarray[idx]=nums[i];                p[i]=true;                robot(idx+1,newarray,nums);                p[i]=false;            }        }  

处理情况3

 接着我还用了一种方式处理。使用了一个单独的变量记录在选择idx的候选值时候,上一个选择的值。

 int preNum = -1; boolean selected = false; for(int i=0;i<nums.length;i++){     if(p[i]==false && (selected==false || (nums[i]!=preNum))){         selected = true;         preNum = nums[i];         newarray[idx]=nums[i];         p[i]=true;         robot(idx+1,newarray,nums);         p[i]=false;     } }   

 总结:大概可以在编程的时候行不通。一定是一定可以才可以。在backtracing中最重要的是理解每一步的状态。

原创粉丝点击