回溯法详解

一、概述

  解决一个回溯问题,实际上就是一个决策树的遍历过程。只需要考虑以下三个问题:

  (1)路径:已经做出的选择。

  (2)选择列表:也就是你当前所做出的选择。

  (3)结束条件:也就是到达决策树底层,无法在做出的条件。

  注意:

  (1)ans为全局变量

  (2)路径最后新建如:ans.add(new ArrayList<>(track))

  (3)track长度是全数还是部分,部分用i+1,全数为cur+1。

二、代码模板

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

  核心:其实就是for循环里面的递归,在递归调用之前做选择,在递归调用之后撤销选择。

三、代码实战

(1)无重复的全排列:非交换

class Solution{
    List<List<Integer>> res=new ArrayList<>();
    public List<List<Integer>>premute(int []nums){
        List<Integer>track=new ArrayList<>();
        backTrack(nums,track); 
        return res;
    }
    
    // 路径:记录在 track 中
    // 选择列表:nums 中不存在于 track 的那些元素
    // 结束条件:nums 中的元素全都在 track 中出现
    public void backTrack(int nums[],List<Integer>track){
        //触发结束条件
        if(nums.length==track.size()){
            res.add(new ArrayList<>(track));
            return;
        }
        for(int i=0;i<nums.length;i++){
            if(track.contains(nums[i])){
                continue;
            }
            track.add(nums[i]);
            backTrack(nums,track);
            track.remove(track.size()-1);
        }
    }
}

(2)无重复排列:使用信号量代替contains方法。

class Solution {
    public List<List<Integer>> permute(int[] nums) {

        List<List<Integer>> res = new ArrayList<>();
        int[] visited = new int[nums.length];
        backtrack(res, nums, new ArrayList<Integer>(), visited);
        return res;

    }

    private void backtrack(List<List<Integer>> res, int[] nums, ArrayList<Integer> tmp, int[] visited) {
        if (tmp.size() == nums.length) {
            res.add(new ArrayList<>(tmp));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (visited[i] == 1) continue;
            visited[i] = 1;
            tmp.add(nums[i]);
            backtrack(res, nums, tmp, visited);
            visited[i] = 0;
            tmp.remove(tmp.size() - 1);
        }
    }
}

(3)无重复的全排列:交换

class Solution {
    List<List<Integer>>res=new ArrayList<>();
    public List<List<Integer>> permute(int[] nums) {
        backTrack(nums,0);
        return res;
    }
    public void backTrack(int[]nums,int cur){
        if(cur==nums.length){
            List<Integer>track=new ArrayList<>();
            for(int i:nums){
                track.add(i);
            }
            res.add(track);
            return;
        }
        for(int i=cur;i<nums.length;i++){
            swap(nums,cur,i);
            backTrack(nums,cur+1);
            swap(nums,cur,i);
        }
    }
    public void swap(int nums[],int i,int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

(4)若排列中可以出现重复元素,求全排列

给定一个可包含重复数字的序列,返回所有不重复的全排列。
输入: [1,1,2]
输出:
[
  [1,1,2],
  [1,2,1],
  [2,1,1]
]

class Solution {
    Set<List<Integer>> set = new HashSet<>();
    public List<List<Integer>> permuteUnique(int[] nums) {
        List<Integer>list=new ArrayList<>();
        backTrack(nums,0);
        List<List<Integer>>res=new ArrayList<>(set);
        return res;
    }
    public void backTrack(int[]nums,int cur){
        if(cur==nums.length){
            List<Integer>track=new ArrayList<>();
            for(int i:nums){
                track.add(i);
            }
            set.add(track);
            return;
        }
        for(int i=cur;i<nums.length;i++){
            swap(nums,cur,i);
            backTrack(nums,cur+1);
            swap(nums,cur,i);
        }
    }
    public void swap(int nums[],int i,int j) {
        int temp = nums[i];
        nums[i] = nums[j];
        nums[j] = temp;
    }
}

 

四、以上都是判断条件都是根据是否cur==nums.length和track.size()==nums.length;当求子集时,该如何操作呢?即track.size() in [0,nums.length];

给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。解集不能包含重复的子集。
输入: nums = [1,2,3]
输出:
[
  [3],
  [1],
  [2],
  [1,2,3],
  [1,3],
  [2,3],
  [1,2],
  []
]

除了迭代法,还有回溯法。

class Solution{
  List<List<Integer>>res=new ArrayList<>();
  int k;
  public List<List<Integer>>subsets(int[] nums){
    List<Integer>track=new ArrayList<>();
    for(k=0;k<nums.length+1;k++){
      backTrack(nums,track,0);
    }
    return res;
  }
  
  public void backTrack(int[] nums,List<Integer>track,int first){
    if(track.size()==k){
      res.add(new ArrayList<>(track));
      return;
    }
    for(int i=first;i<nums.length;i++){
      track.add(nums[i]);
      backTrack(nums,track,i+1);
      track.remove(track.size()-1);
    }
  }
}

 

posted on 2020-03-22 21:40  hdc520  阅读(582)  评论(0编辑  收藏  举报

导航