lc46. 全排列(MD)

给定一个不含重复数字的数组 nums ,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。

示例 1:

输入:nums = [1,2,3]
输出:[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]]

示例 2:

输入:nums = [0,1]
输出:[[0,1],[1,0]]

示例 3:

输入:nums = [1]
输出:[[1]]

提示:

  • 1 <= nums.length <= 6
  • -10 <= nums[i] <= 10
  • nums 中的所有整数 互不相同

因为要求返回所有可能的全排列,所以要考虑是否会出现重复遍历的情况

class Solution {
private List<List<Integer>> res = new ArrayList<>();
private List<Integer> path = new ArrayList<>();
private boolean[] used;

public List<List<Integer>> permute(int[] nums) {
used = new boolean[nums.length];
dfs(nums);
return res;
}

private void dfs(int[] nums) {
if (path.size() == nums.length) {
res.add(new ArrayList<>(path));
return;
}
for (int i = 0; i < nums.length; i++) {
if (used[i]) {
continue;
}
used[i] = true;
path.add(nums[i]);
dfs(nums);
path.removeLast();
used[i] = false;
}
}
}

lc39. 组合总和(MD)

给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target ,找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。

candidates 中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。

对于给定的输入,保证和为 target 的不同组合数少于 150 个。

示例 1:

输入:candidates = [2,3,6,7], target = 7
输出:[[2,2,3],[7]]
解释:
2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。
7 也是一个候选, 7 = 7 。
仅有这两种组合。

示例 2:

输入: candidates = [2,3,5], target = 8
输出: [[2,2,2,2],[2,3,3],[3,5]]

示例 3:

输入: candidates = [2], target = 1
输出: []

提示:

  • 1 <= candidates.length <= 30
  • 2 <= candidates[i] <= 40
  • candidates 的所有元素 互不相同
  • 1 <= target <= 40

请注意这道题和其他回溯题不同的一点:数字可以被无限次选取,所以每次遍历

有点类似于贪心,但有点不太像。我们为了能够减少单次循环的遍历次数,可以先对数组做一次从小到大的排序,这样如果出现现有的数字之和 + 待选数字 > 目标总和的情况,就可以直接break掉

至于其他的内容,就和传统的回溯题一样了

class Solution {
private List<List<Integer>> res = new ArrayList<>();
private List<Integer> path = new ArrayList<>();

public List<List<Integer>> combinationSum(int[] candidates, int target) {
Arrays.sort(candidates);
dfs(candidates, target, 0, 0);
return res;
}

private void dfs(int[] candidates, int target, int idx, int sum) {
if (target == sum) {
res.add(new ArrayList<>(path));
return;
}
for (int i = idx; i < candidates.length; i++) {
if (sum + candidates[i] > target) {
break;
}
sum += candidates[i];
path.add(candidates[i]);
dfs(candidates, target, i, sum);
sum -= candidates[i];
path.removeLast();
}
}
}

lc52. N 皇后 II(HD)

n 皇后问题 研究的是如何将 n 个皇后放置在 n × n 的棋盘上,并且使皇后彼此之间不能相互攻击。

给你一个整数 n ,返回 n 皇后问题 不同的解决方案的数量。

示例 1:

img

输入:n = 4
输出:2
解释:如上图所示,4 皇后问题存在两个不同的解法。

示例 2:

输入:n = 1
输出:1

提示:

  • 1 <= n <= 9

经典的n皇后问题,当然也是校招面试中常见的hard题

首先分析题目要求:n皇后要求不能有两个棋子在同一列/行/斜线上。所以我们要考虑用一种数据结构来存储具体哪一行/列/斜线放了棋子,那么之后这些行/列/斜线就不能再放置棋子了

考虑到这个要求,我们用 Set 这个数据结构来存储棋子的行/列/斜线

在每次放置棋子之前都要先用这三个数据结构来判断有没有棋子已经放在了和这个格子同一行/列或者同一条斜线上,如果有那么就不能再放置;反之就可以放置,然后做一次dfs遍历

class Solution {
public int totalNQueens(int n) {
Set<Integer> columns = new HashSet<>();
Set<Integer> diagonals1 = new HashSet<>();
Set<Integer> diagonals2 = new HashSet<>();
return dfs(n, 0, columns, diagonals1, diagonals2);
}

private int dfs(int n, int row, Set<Integer> columns, Set<Integer> diagonals1, Set<Integer> diagonals2) {
if (row == n) {
return 1;
} else {
int cnt = 0;
for (int i = 0; i < n; i++) {
if (columns.contains(i)) {
continue;
}
int diagonal1 = row - i;
int diagonal2 = row + i;
if (diagonals1.contains(diagonal1)) {
continue;
}
if (diagonals2.contains(diagonal2)) {
continue;
}
columns.add(i);
diagonals1.add(diagonal1);
diagonals2.add(diagonal2);
cnt += dfs(n, row + 1, columns, diagonals1, diagonals2);
columns.remove(i);
diagonals1.remove(diagonal1);
diagonals2.remove(diagonal2);
}
return cnt;
}
}
}

看似很复杂,其实也没那么难,本质上还是回溯