Home » Uncategorized » Backtracking: subsets, permutation, combination, solve sudoko

Backtracking: subsets, permutation, combination, solve sudoko

两种办法DFS,recursive或者iterative。
一般需要skip重复元素的时候,recursive更容易处理。

Subsets I II
iterative和recursive都比较好想,都是在previous结果的基础上加入新元素生成新的结果加入result。II中处理重复 1,2,2:null,1,2,12,[22,122]--就是因为对null 1,第一个2已经加过2了,所以第二个2就不用处理null和1了,直接在2和12的基础上处理,生成22,122.

    public List<List<Integer>> subsetsWithDup(int[] S) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        Arrays.sort(S);
        result.add(new ArrayList<Integer>());
        int skip = 0, j = 0;
        for (int i = 0; i < S.length; i++) {
            List<List<Integer>> list = new ArrayList<List<Integer>>();
            if (i > 0 && S[i] == S[i-1])
                j = skip;
            else
                j = 0;
            skip = result.size();
            for (; j < result.size(); j++) {
                List<Integer> tmp = new ArrayList<Integer>(result.get(j));
                tmp.add(S[i]);
                list.add(tmp);
            }
            result.addAll(list);
        }
        return result;
	}

Permutations I, II
这个题的iterative是在前面生成的每个结果的每个空档插入新元素:1,2 =》 3,1,2;1,3,2;1,2,3

    public List<List<Integer>> permute(int[] num) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        res.add(new ArrayList<Integer>());
        for (int i = 0; i < num.length; i++) {
            List<List<Integer>> newRes = new ArrayList<List<Integer>>();
            for (List<Integer> ls: res) {
                for (int j = 0; j < ls.size()+1; j++) {
                    ls.add(j, num[i]);
                    newRes.add(new ArrayList<Integer>(ls));
                    ls.remove(j);
                }
            }
            res = new ArrayList<List<Integer>>(newRes);//new object
        }
        return res;
    }

recursive: 在已经生成的串上面重新安排位置:从1,2,。。k位置取出来,然后放在尾部。这个题目的II只会recursive。重复元素只要跳过就好,因为不用重新安排位置。

    public List<List<Integer>> permuteUnique(int[] num) {
        List<List<Integer>> res = new ArrayList<List<Integer>>();
        if(num.length == 0) return res;
        List<Integer> nums = new ArrayList<Integer>();
        Arrays.sort(num);
        for(int i: num)
        	nums.add(i);
        perm(nums, new ArrayList<Integer>(), res);
        return res;
    }
    
    public void perm(List<Integer> num, List<Integer> cur, List<List<Integer>> res) {
        if(num.isEmpty()) res.add(new ArrayList<Integer>(cur));
        else {
            for(int i = 0; i < num.size(); i++) {
                List<Integer> next = new ArrayList<Integer>(num);
                next.remove(i);
                cur.add(num.get(i));
                perm(next, cur, res);
                cur.remove(cur.size()-1);
                while(i+1 < num.size() && num.get(i) == num.get(i+1)) {
                	i++;
                }
                	
            }
        }
    }

Permutation Sequence
这个虽然是数学题目,但是需要深入理解permute的本质:每个位置用一个元素会生成(n-1)!个可能元素。所以iterative处理每个位置。

    public String getPermutation(int n, int k) {
        int index = 1;
        List<Integer> nums = new ArrayList<Integer>();
        for (int i = 1; i <= n; i++)
            nums.add(i);
        k--;
        k = k % n;
        k++;
        return perm(nums, new ArrayList<Integer>(), k);
	}
	private String perm(List<Integer> nums, List<Integer> cur, int k) {
	    if (k == 1) {
	        String res = "";
	        for(int num: nums)
	            res += String.valueOf(num);
	        return res;
	    }
	    String res = "";
	    for (int i = 0; i < nums.size(); i++) {
	        List<Integer> tmp = new ArrayList<Integer>(nums);
	        tmp.remove(i);
	        cur.add(nums.get(i));
	        k-=1;
            res = perm(nums, cur, k);
            if (k == 1)
                break;
            cur.remove(cur.size()-1);
	    }
	    return res;
	}

Combinations n取k个元素
两层循环,外层控制k,内层控制取元素:便利所有可能,就是 j = start + 1; j <= n – k + 1 + i

    public List<List<Integer>> combine(int n, int k) {
        List<List<Integer>> result = new ArrayList<List<Integer>>();
        if (n < k) return result;
        result.add(new ArrayList<Integer>());
        for (int i = 0; i < k; i++) {
            List<List<Integer>> pre = new ArrayList<List<Integer>>();
            for (List<Integer> item: result) {
                int start = 0;
                if (item.size() > 0)
                    start = item.get(item.size()-1);
                for (int j = start + 1; j <= n - k + 1 + i; j++) {
                    List<Integer> newItem = new ArrayList<Integer>(item);
                    newItem.add(j);
                    pre.add(newItem);
                }
            }
            result = new ArrayList<List<Integer>>(pre); // create new here
        }
        return result;
    }

Combination Sum II
典型DFS,走不通就不走了,走通就加入result。
两个要点:
1.dfs里调用下一层dfs的时候,仍然使用i而不是i+1,这是因为可以重复取元素。
2.重复元素直接skip

public List<List<Integer>> combinationSum2(int[] candidates, int target) {
	List<List<Integer>> result = new ArrayList<List<Integer>>();
	List<Integer> tmpResult = new ArrayList<Integer>();
	Arrays.sort(candidates);
	dfs(candidates, target, tmpResult, result, 0);
	return result;
}

private void dfs(int[] candidates, int target, List<Integer> tmpResult, List<List<Integer>> result, int index) {
	if (target == 0) {
		result.add(new ArrayList<Integer>(tmpResult));
		return;
	}
	if (index == candidates.length || target < 0) return;
	
	for (int i = index; i < candidates.length; i++) {
		int val = candidates[i];
	    if(i > index && candidates[i-1] == val) continue;
		tmpResult.add(val);
		dfs(candidates, target - val, tmpResult, result, i+1);
		tmpResult.remove(tmpResult.size()-1);
	}
}

Sudoku Solver
这个题类似N Queen
不过这里用set保存所有不可用的元素,然后用剩下可用的继续dfs。这次代码比较简洁

    public void solveSudoku(char[][] board) {
        solve(board);
    }
    
    private boolean solve(char[][] board) {
        for(int row = 0; row < 9; row++) {
            for(int col = 0; col < 9; col++) {
                if (board[row][col] != '.')
                    continue;
                Set<Integer> set = new HashSet<Integer>();
                for (int i = 0; i < 9; i++) {
                    if (board[row][i] != '.')
                        set.add(board[row][i]-'0');
                    if (board[i][col] != '.')
                        set.add(board[i][col]-'0');
                }
                
                int startRow = row/3*3, startCol = col/3*3;
                for (int j = 0; j < 9; j++) {
                    if (board[startRow + j/3][startCol + j%3] != '.')
                        set.add(board[startRow + j/3][startCol + j%3]-'0');
                }
                for (int k = 1; k < 10; k++) {
                    if (!set.contains(k)) {
                        board[row][col] = (char)(k + '0');
                        if (solve(board))
                            return true;
                    }
                }
                
                board[row][col] = '.';
                return false;
                
            }
        }
        return true;
    }

Leave a comment