BackTracking
重点在于如何构建logic tree
Last updated
Was this helpful?
重点在于如何构建logic tree
Last updated
Was this helpful?
构建logic tree, 方便推算Time complexity
好题
permutations II
subset II
区分combination-->permutation-->subset不同的出口
subset是combination的一种
combination[1,2,3]和[3,2,1]是一样的,所以需要startIdx来track
permutation中[1,2,3]和[3,2,1]不一样,所以always start from 0
Generate parentheses
Generalized Abbreviation
N queens
Word search ---不能死套模板
Permutation类的题目
generate all permutations ----> permutations
next permutation
generate the permutation number K ----> permutation sequence
建立一个visited[] 来记录每个nums[i]是否被用过
class Solution {
public List<List<Integer>> permute(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null ) {
return res;
}
Arrays.sort(nums); //组合类搜索的模板,去重时需要
boolean[] visited = new boolean[nums.length];
dfs(res, new ArrayList<Integer>(), nums, visited);
return res;
}
//递归定义:找到所有以permutation开头的排列,加到res里
private void dfs(List<List<Integer>> res, ArrayList<Integer> permutation, int[] nums, boolean[] visited) {
//出口
if(permutation.size() == nums.length) {
res.add(new ArrayList<Integer>(permutation));
return;
}
//拆解
for (int i = 0; i < nums.length; i++) {
if(visited[i]) {
continue;
}
//[1]->[1,2]
permutation.add(nums[i]);
visited[i] = true;
dfs(res, permutation, nums, visited);
//[1,2] ->[1]
permutation.remove(permutation.size() - 1);
visited[i] = false;
//then next iteration[1]->[1,3]
}
}
}
去重的题一般都需要先sort array, 然后再check duplicate
if (i > 0 && nums[i] == nums[i - 1] && !visited[i-1]) continue;
class Solution {
public List<List<Integer>> permuteUnique(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null) {
return res;
}
Arrays.sort(nums); //组合类搜索的模板,去重时需要
boolean[] visited = new boolean[nums.length];
dfs(res, new ArrayList<Integer>(), nums, visited);
return res;
}
//递归定义:找到所有以permutation开头的排列,加到res里
private void dfs(List<List<Integer>> res, ArrayList<Integer> permutation, int[] nums, boolean[] visited) {
//出口
if(permutation.size() == nums.length) {
res.add(new ArrayList<Integer>(permutation));
return;
}
//拆解
for (int i = 0; i < nums.length; i++) {
if(visited[i] || (i > 0 && nums[i] == nums[i - 1] && !visited[i-1])) {
continue;
}
//[1]->[1,2]
permutation.add(nums[i]);
visited[i] = true;
dfs(res, permutation, nums, visited);
//[1,2] ->[1]
permutation.remove(permutation.size() - 1);
visited[i] = false;
//then next iteration[1]->[1,3]
}
}
}
不适合做面试题
康托展开的应用
如n=5,x=96时:
首先用96-1得到95,说明x之前有95个排列.(将此数本身减去1)
用95去除4! 得到3余23,说明有3个数比第1位小,所以第一位是4.
用23去除3! 得到3余5,说明有3个数比第2位小,所以是4,但是4已出现过,因此是5.
用5去除2!得到2余1,类似地,这一位是3.
用1去除1!得到1余0,这一位是2. 最后一位只能是1.
所以这个数是45321. 按以上方法可以得出通用的算法。
public String getPermutation(int n, int k) {
StringBuilder sb = new StringBuilder();
boolean[] used = new boolean[n];
k = k - 1;
int factor = 1;
for (int i = 1; i < n; i++) {
factor *= i; //(n-1) !
}
for (int i = 0; i < n; i++) {
int index = k / factor;
k = k % factor;
for (int j = 0; j < n; j++) {
if (used[j] == false) {
if (index == 0) {
sb.append(j + 1);
used[j] = true;
break;
} else {
index--;
}
}
}
if (i < n - 1) {
factor /= n - 1 - i;
}
}
return sb.toString();
}
给定n和k, 从1~n中选K个数,返回所有结果
class Solution {
public List<List<Integer>> combine(int n, int k) {
List<List<Integer>> res = new ArrayList<>();
if (n == 0 || k == 0 || k > n) {
return res;
}
dfs(res, new ArrayList<Integer>(), n, k, 1);
return res;
}
//递归定义:找到所有以combination开头的组合,加到res里
private void dfs(List<List<Integer>> res, ArrayList<Integer> combination, int n, int k, int start) {
//出口
if(combination.size() == k) {
res.add(new ArrayList<Integer>(combination));
return;
}
//拆解
for (int i = start; i <= n; i++) {
//[1]->[1,2]
combination.add(i);
dfs(res, combination, n, k, i + 1);
//[1,2] ->[1]
combination.remove(combination.size() - 1);
//then next iteration[1]->[1,3]
}
}
}
no duplicate num
same num can be chosen unlimited times
可选可不选就是for loop to iterate each element as starting index
class Solution {
//all numbers are positive?
public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
List<List<Integer>> res = new ArrayList<>();
List<Integer> list = new ArrayList<>();
helper(res, list, candidates, 0, 0, target);
return res;
}
private void helper(List<List<Integer>> res, List<Integer> list, int[] candidates, int start, int sum, int target) {
if (sum > target || start == candidates.length) {
return;
}
if (sum == target) {
res.add(new ArrayList<>(list));
}
//可选可不选就是for loop to iterate each element as starting index
for (int i = start; i < candidates.length; i++) {
list.add(candidates[i]);
helper(res, list, candidates, i, sum + candidates[i], target);
list.remove(list.size() - 1);
}
}
}
dup num
each num can only be used once
class Solution {
public List<List<Integer>> combinationSum2(int[] candidates, int target) {
List<List<Integer>> res = new ArrayList<>();
List<Integer> list = new ArrayList<>();
Arrays.sort(candidates);
boolean[] visited = new boolean[candidates.length];
helper(res, list, candidates, visited, 0, 0, target);
return res;
}
private void helper(List<List<Integer>> res, List<Integer> list, int[] candidates, boolean[] visited, int start, int sum, int target) {
//return
if (sum > target) {
return;
}
if (sum == target) {
res.add(new ArrayList<>(list));
}
//iteration
for (int i = start; i < candidates.length; i++) {
//check dup
if (i > 0 && candidates[i-1] == candidates[i] && !visited[i-1]) continue;
list.add(candidates[i]);
visited[i] = true;
helper(res, list, candidates, visited, i + 1, sum + candidates[i], target);
list.remove(list.size() - 1);
visited[i] = false;
}
}
}
Combination sum III
1-9里选K个数sum to N
Combination Sum IV
all positive numbers, no dup
find num of combinations that sum to target
DP
class Solution {
public List<List<Integer>> subsetsWithDup(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length == 0) {
return res;
}
Arrays.sort(nums); //组合类搜索的模板,去重时需要
int[] visited = new int[nums.length];
dfs(res, new ArrayList<Integer>(), nums, 0, visited);
return res;
}
//递归定义:append all subsets from index idx to the subset,
private void dfs(List<List<Integer>> res, ArrayList<Integer> subset, int[] nums, int idx, int[] visited) {
res.add(new ArrayList<Integer>(subset));
//拆解
for (int i = idx; i < nums.length; i++) {
//make sure to visit the duplicate number only when previous has been visited
if (i > 0 && nums[i] == nums[i - 1] && visited[i - 1] == 0) {
continue;
}
//[1]->[1,2]
subset.add(nums[i]);
visited[i] = 1;
dfs(res, subset, nums, i + 1,visited);
//[1,2] ->[1]
subset.remove(subset.size() - 1);
visited[i] = 0;
//then next iteration[1]->[1,3]
}
}
}
先想high level的框架,如何能套用dfs模板,不用着急去实现drawBoard()
想清楚isValid() logic
九章模板清晰好用!
class Solution {
public List<List<String>> solveNQueens(int n) {
List<List<String>> res = new ArrayList<>();
if (n < 1) {
return res;
}
List<Integer> cols = new ArrayList<>();
dfs(res, cols, n);
return res;
}
private void dfs(List<List<String>> res, List<Integer> cols, int n) {
if (cols.size() == n) {
res.add(drawBoard(cols));
return;
}
for (int i = 0; i < n; i++) {
if (!isValid(cols, i)) {
continue;
}
cols.add(i);
dfs(res, cols, n);
cols.remove(cols.size() - 1);
}
}
private List<String> drawBoard(List<Integer> cols) {
List<String> board = new ArrayList<>();
for (int i = 0; i < cols.size(); i++) {
StringBuilder sb = new StringBuilder();
for (int j = 0; j < cols.size(); j++) {
if (j == cols.get(i)) {
sb.append('Q');
} else {
sb.append('.');
}
}
board.add(sb.toString());
}
return board;
}
private boolean isValid(List<Integer> cols, int col) {
int row = cols.size();
for (int i = 0; i < cols.size(); i++) {
int j = cols.get(i);
if (col == j) { //same col
return false;
}
if (j - i == col - row) {//left diagonal
return false;
}
if (j + i == col + row){//right diagonal
return false;
}
}
return true;
}
}
backtracking必练题
需要灵活套用backtracking模板
想清楚递归三要素
如何定义for loop, 如何定义递归函数
class Solution {
public boolean exist(char[][] board, String word) {
if(word == null || word.length() == 0 || board == null || board.length == 0 || board[0].length == 0) {
return false;
}
int row = board.length;
int col = board[0].length;
boolean[][] visited = new boolean[row][col];
for (int i = 0; i < board.length; i++) {
for (int j = 0; j < board[0].length; j++) {
if (isExist(board, visited, i, j, 0, word)) {
return true;
}
}
}
return false;
}
private boolean isExist(char[][] board, boolean[][] visited, int i, int j, int pos, String word) {
//出口
if (pos == word.length()) {
return true;
}
if (i < 0 || i >= board.length || j < 0 || j >=board[0].length || visited[i][j] || board[i][j] != word.charAt(pos)) {
return false;
}
//调用
visited[i][j] = true;
//move up
if (isExist(board, visited, i - 1, j, pos + 1, word)) {
return true;
}
//move down
if (isExist(board, visited, i + 1, j, pos + 1, word)) {
return true;
}
//move left
if (isExist(board, visited, i, j - 1, pos + 1, word)) {
return true;
}
//move right
if (isExist(board, visited, i, j + 1, pos + 1, word)) {
return true;
}
//backtrack
visited[i][j] = false;
return false;
}
}
helper(List res, StringBuilder sb, int left, int right, int n)
left
tracks number of '(' has been placed
right
tracks number of ') hash been placed
when to consider place another '(' --> when left < n
when to consider place another ')' --> when left > right
class Solution {
public List<String> generateParenthesis(int n) {
List<String> res = new ArrayList<>();
helper(res, new StringBuilder(), 0, 0, n);
return res;
}
private void helper(List<String> res, StringBuilder sb, int left, int right, int n) {
//return
if (left == n && right == n) {
res.add(sb.toString());
return;
}
int len = sb.length();
//consider add '(' only when left < n
if (left < n) {
sb.append('(');
helper(res, sb, left + 1, right, n);
sb.setLength(len);
}
//consider add ')' only when left > right
if (left > right) {
sb.append(')');
helper(res, sb, left, right + 1, n);
sb.setLength(len);
}
}
}
典型的backtracking题
切记任何时候调用sb.append()完都需要sb.setLength(len), 尤其是递归的出口add result的时候
如果是直接res.add(sb.toString()), 没有任何sb.append()操作的话可以 early return
class Solution {
public List<String> generateAbbreviations(String word) {
List<String> res = new ArrayList<>();
helper(res, word.toCharArray(), new StringBuilder(), 0, 0);
return res;
}
private void helper(List<String> res, char[] word, StringBuilder sb, int curNum, int idx) {
//return
if (idx == word.length) {
if (curNum > 0) {
sb.append(curNum);
}
res.add(sb.toString());
return;
}
int len = sb.length();
//no use char
helper(res, word, sb, curNum + 1, idx + 1);
sb.setLength(len);
//use char of word[idx]
if (curNum > 0) {
sb.append(curNum);
sb.append(word[idx]);
helper(res, word, sb, 0, idx + 1); //reset curNum to 0
} else {
sb.append(word[idx]);
helper(res, word, sb, 0, idx + 1);
}
sb.setLength(len);
}
}