回溯法

部分转自:https://blog.csdn.net/goforitaaa/article/details/80445404

回溯法

回溯法有“通用解题法”之称,用它可以系统的搜索问题的所有解。通俗的说,用回溯法可以找到问题的所有解

它在问题的解空间树中,按照深度优先搜索策略,从根节点出发搜索解空间树。算法搜索至解空间树的任一节点时,先判断该节点是否包含问题的解,如果肯定不包含,则跳过对以此改节点为根的子树的搜索逐层向其祖先进行回溯;否则,进入该子树,继续按照深度优先搜索策略。

回溯发的算法框架和基本思想

1,明确定义问题的解空间(就是问题的所有可能的解)

2,得到问题的解空间后,将解空间很好的组织起来,使得能方便用回溯法搜索整个解空间。(一般将解空间组织成树或者图的    )形式

3,确定了解空间的组织结构后,回溯法从开始节点(根节点)出发,以深度优先搜索整个解空间。这个开始节点称为活节点,同时也成为当前的扩展节点。在当前扩展节点处,搜索向纵深方向移至一个新节点。这个新节点成为新的活节点并且成为当前扩展节点。如果在当前扩展节点处不能再向纵深方向移动(即叶节点),则当前扩展节点就成为死节点。此时,应往回移动至最近的活节点处,并使这个活节点为当前的扩展节点。直至找到所有的解或者解空间中已经无活节点为止。

回溯法基本思想以及此处标红部分一定要充分理解。

N后问题

问题描述:

在nxn的棋盘上放置彼此不受攻击的n个皇后。按照国际象棋的规则,皇后可以攻击与之处在同一行或同一列或同一斜线上的棋子。n后问题等价于在n x n的棋盘上放着n个皇后,任何两个皇后不放在同一列同一行,或同一斜线。

算法设计:

用x[i]表示第i个皇后放在棋盘的第i行的第x[i]列。由问题得,两个皇后的位置分别是(i,j)和(k,l)不能放在同一列,即x[i] 不等于x[j], 不在同一斜线 即 k=| l-j/k-i | 不等于1.

所有可能的解为 n的n次方,

组织:为一个完全n叉树

回溯法_第1张图片

回溯法

package edu.xatu;

public class NQueen1 {

    static int n; // 皇后个数

    static int[] x; // 当前解

    static long sum; // 当前找到的可行方案数

 

    public static long nQueen(int nn) {

        n = nn;

        sum = 0;

        x = new int[n + 1];

        for (int i = 0; i <= n; i++)

            x[i] = 0;

        backtrack(1);

        return sum;

    }

 

    private static boolean place(int k) {// 判断皇后是否能放入k列

        for (int j = 1; j < k; j++) { // 与前k-1个皇后的位置比较

            if ((Math.abs(k - j) == Math.abs(x[j] - x[k])) || (x[j] == x[k])) // 同对角线或同列

                return false;

        }

        return true;

    }

 

    private static void backtrack(int t) {

        if (t > n) {

            sum++;

            for (int i = 1; i <= n; i++)

                // 输出当前方案

                System.out.printf(“%5d”, x[i]);

            System.out.println();

        } else

            for (int i = 1; i <= n; i++) {

                x[t] = i; // 把第t个皇后依次放入n个格子,看是否可行

                if (place(t)) // 可行就继续放第t+1个皇后

                    backtrack(t + 1);

            }

    }

 

    // 测试

    public static void main(String[] args) {

        System.out.println(“n皇后问题方案可行数为:” + nQueen(4));

    }

}

运行结果:

回溯法_第2张图片


This structure might apply to many other backtracking questions, but here I am just going to demonstrate Subsets, Permutations, and Combination Sum.

Subsets : https://leetcode.com/problems/subsets/

public List<List> subsets(int[] nums) {
    List<List> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, 0);
    return list;
}

private void backtrack(List<List> list , List tempList, int [] nums, int start){
    list.add(new ArrayList<>(tempList));
    for(int i = start; i < nums.length; i++){
        tempList.add(nums[i]);
        backtrack(list, tempList, nums, i + 1);
        tempList.remove(tempList.size() - 1);
    }
}

Subsets II (contains duplicates) : https://leetcode.com/problems/subsets-ii/

public List<List> subsetsWithDup(int[] nums) {
    List<List> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, 0);
    return list;
}

private void backtrack(List<List> list, List tempList, int [] nums, int start){
    list.add(new ArrayList<>(tempList));
    for(int i = start; i < nums.length; i++){
        if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates
        tempList.add(nums[i]);
        backtrack(list, tempList, nums, i + 1);
        tempList.remove(tempList.size() - 1);
    }
} 

Permutations : https://leetcode.com/problems/permutations/

public List<List> permute(int[] nums) {
   List<List> list = new ArrayList<>();
   // Arrays.sort(nums); // not necessary
   backtrack(list, new ArrayList<>(), nums);
   return list;
}

private void backtrack(List<List> list, List tempList, int [] nums){
   if(tempList.size() == nums.length){
      list.add(new ArrayList<>(tempList));
   } else{
      for(int i = 0; i < nums.length; i++){ 
         if(tempList.contains(nums[i])) continue; // element already exists, skip
         tempList.add(nums[i]);
         backtrack(list, tempList, nums);
         tempList.remove(tempList.size() - 1);
      }
   }
} 

Permutations II (contains duplicates) : https://leetcode.com/problems/permutations-ii/

public List<List> permuteUnique(int[] nums) {
    List<List> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, new boolean[nums.length]);
    return list;
}

private void backtrack(List<List> list, List tempList, int [] nums, boolean [] used){
    if(tempList.size() == nums.length){
        list.add(new ArrayList<>(tempList));
    } else{
        for(int i = 0; i < nums.length; i++){
            if(used[i] || i > 0 && nums[i] == nums[i-1] && !used[i - 1]) continue;
            used[i] = true; 
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, used);
            used[i] = false; 
            tempList.remove(tempList.size() - 1);
        }
    }
}

Combination Sum : https://leetcode.com/problems/combination-sum/

public List<List> combinationSum(int[] nums, int target) {
    List<List> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, target, 0);
    return list;
}

private void backtrack(List<List> list, List tempList, int [] nums, int remain, int start){
    if(remain < 0) return;
    else if(remain == 0) list.add(new ArrayList<>(tempList));
    else{ 
        for(int i = start; i < nums.length; i++){
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, remain - nums[i], i); // not i + 1 because we can reuse same elements
            tempList.remove(tempList.size() - 1);
        }
    }
}

Combination Sum II (can’t reuse same element) : https://leetcode.com/problems/combination-sum-ii/

public List<List> combinationSum2(int[] nums, int target) {
    List<List> list = new ArrayList<>();
    Arrays.sort(nums);
    backtrack(list, new ArrayList<>(), nums, target, 0);
    return list;

}

private void backtrack(List<List> list, List tempList, int [] nums, int remain, int start){
    if(remain < 0) return;
    else if(remain == 0) list.add(new ArrayList<>(tempList));
    else{
        for(int i = start; i < nums.length; i++){
            if(i > start && nums[i] == nums[i-1]) continue; // skip duplicates
            tempList.add(nums[i]);
            backtrack(list, tempList, nums, remain - nums[i], i + 1);
            tempList.remove(tempList.size() - 1); 
        }
    }
} 

Palindrome Partitioning : https://leetcode.com/problems/palindrome-partitioning/

public List<List<String>> partition(String s) {
   List<List<String>> list = new ArrayList<>();
   backtrack(list, new ArrayList<>(), s, 0);
   return list;
}

public void backtrack(List<List<String>> list, List<String> tempList, String s, int start){
   if(start == s.length())
      list.add(new ArrayList<>(tempList));
   else{
      for(int i = start; i < s.length(); i++){
         if(isPalindrome(s, start, i)){
            tempList.add(s.substring(start, i + 1));
            backtrack(list, tempList, s, i + 1);
            tempList.remove(tempList.size() - 1);
         }
      }
   }
}

public boolean isPalindrome(String s, int low, int high){
   while(low < high)
      if(s.charAt(low++) != s.charAt(high--)) return false;
   return true;
} 

你可能感兴趣的:(Algorithm)