持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第19天,点击查看活动详情
本文包含以下几题:
- 给定一个不包含重复数字的数组
nums,返回其所有可能的全排列。 - 给定一个包含重复数字的数组
nums,返回其所有可能的全排列。 - N皇后问题。根据国际象棋的规则,皇后可以攻击同处于同一行或同一列或者同一对角线上的棋子,给定一个整数
n,返回所有不同的n皇后解决方案。
解题思路
LeetCode 46 全排列
本题是通过回溯寻找二叉树上的所有叶子节点,和之前回溯区别在于本题不需要确定起始索引,但在回溯过程中需要判断当前数字是否已经被使用,如果被使用则跳过,回溯终止条件为临时集合的长度等于数组长度,可得代码如下:
private List<List<Integer>> resPer = new ArrayList<>();
public List<List<Integer>> permute(int[] nums) {
boolean[] visited = new boolean[nums.length];
backtraceP(nums, new ArrayList<>(), visited);
return resPer;
}
public void backtraceP(int[] nums, ArrayList<Integer> temp, boolean[] visited){
if(temp.size()==nums.length){
resPer.add(new ArrayList<>(temp));
return;
}
for(int i=0;i<nums.length;i++){
if(visited[i]) continue;
temp.add(nums[i]);
visited[i] = true;
backtraceP(nums, temp, visited);
temp.remove(temp.size()-1);
visited[i] = false;
}
}
LeetCode 47 全排列II
本题是上题的变体,区别就是数组中包含相同的数字,那在上一题的基础上就需要做去重,去重的思路是二叉树的同层元素不能相同,因为不考虑输出顺序,因此可以使用之前的对数组排序+used数组的方法进行去重,另一个方法就是本题已经确定给定的数组范围是-10-10,因此可以使用长度为21的数组进行记录使用情况,下面的思路为第二种,代码如下:
private List<List<Integer>> resPermu = new ArrayList<>();
public List<List<Integer>> permuteUnique(int[] nums) {
boolean[] visited = new boolean[nums.length];
backtracePer(nums, new ArrayList<>(), visited);
return resPermu;
}
public void backtracePer(int[] nums, ArrayList<Integer> temp, boolean[] visited){
if(temp.size()==nums.length){
resPermu.add(new ArrayList<>(temp));
return;
}
int[] used = new int[21];
for(int i=0;i<nums.length;i++){
if(visited[i]||used[nums[i]+10]==1) continue;
temp.add(nums[i]);
used[nums[i]+10]=1;
visited[i] = true;
backtracePer(nums, temp, visited);
temp.remove(temp.size()-1);
visited[i] = false;
}
}
LeetCode 51 N皇后
N皇后问题要求不能同行同列且同对角线,那如何使用回溯是一个较难的问题,不能同行同列就意味着每行每列都最多只存在一个元素,那想象一下回溯的多叉树,如果选定了第一行的某个元素位置,那下一个位置只能在第二行选择,同理如下。
即我们可以选择多叉树的高度为棋盘的高度,而同一层子树的选择则为某行的所有列,此时就可以进行回溯了。回溯重要的条件是找到回溯终止条件,此处很显然终止条件为高度超出棋盘行,而判断是否同行同列很简单,斜线我们除了需要判断45度的还需要判断135度的。
整体分析可得代码如下:
private List<List<String>> resSo = new ArrayList<>();
public List<List<String>> solveNQueens(int n) {
char[][] c = new char[n][n];
for (char[] c1 : c) {
Arrays.fill(c1, '.');
}
backtrace(n, 0, c);
return resSo;
}
public void backtrace(int n, int curRow, char[][] c){
if(curRow == n){
ArrayList<String> temp = new ArrayList<>();
for (char[] c1 : c) {
temp.add(String.valueOf(c1));
}
resSo.add(temp);
return;
}
for(int i=0;i<n;i++){
if(isProper(c, curRow, i, n)){
c[curRow][i] = 'Q';
backtrace(n, curRow+1, c);
c[curRow][i] = '.';
}
}
}
public boolean isProper(char[][] c, int row, int column, int n){
// 检查行
for(int i=0;i<n;i++){
if(c[i][column]=='Q') return false;
}
// 检查列
for(int i=0;i<n;i++){
if(c[row][i]=='Q') return false;
}
// 检查45度对角
for(int i=row-1, j=column-1;i>=0&&j>=0;i--,j--){
if(c[i][j] == 'Q') return false;
}
// 检查135度对角
for(int i=row-1, j=column+1;i>=0&&j<n;i--,j++){
if(c[i][j] == 'Q') return false;
}
return true;
}